Swift 4: Timer Crashing - Unrecognized Selector Sent to Instance

Swift 4 Timer Crashes with NSException

As Andrea said, you should instantiate the timer in viewDidLoad. Also the documentation says:

The selector should have the following signature: timerFireMethod: (including a colon to indicate that the method takes an argument).

And don't forget to disable this timer in, for example, viewDidDisappear. You can't invalidate it in deinit because the repeating timer keeps a strong reference to its target, and your deinit will not get called as long as the timer is running. And if you remove it in viewDidDisappear, you might want to create the timer in viewDidAppear.

Thus, resulting in something like:

class ViewController: UIViewController {

weak var timer: Timer?

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(tick(_:)), userInfo: nil, repeats: true)
}

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)

timer?.invalidate()
}

@objc func tick(_ timer: Timer) {
print("tick")
}

}

Or you can use the block-based rendition, with [weak self] pattern, and the timer won't keep a strong reference to the view controller, in which case you can use deinit:

class ViewController: UIViewController {

var timer: Timer?

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] timer in // the `[weak self] reference is only needed if you reference `self` in the closure
print("tick")
}
}

deinit {
timer?.invalidate()
}

}

unrecognized selector sent to instance-Swift

In target-action pattern, the target object needs to implement the action method.

But in your code:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

You use self as target, which is an instance of MapVC, that does not implement the method downloadBusData(userId:warning:delegate:completed:).

When you specify #selector(someInstance.methodName(...)) for the action method, you need to pass someInstance to the target object. In your case someInstance is self.busInfoObject.

Which means the line creating a Timer should become like this:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self.busInfoObject, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

But this does not work.


I was stupid enough that I have almost forgotten to tell you another important thing in target-action pattern.

Which is,

The signature of the action method is definitely fixed according to the target.

When using Timer, the signature needs to be the same as this:

class func scheduledTimer(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool) -> Timer

- (void)timerFireMethod:(NSTimer *)timer

The notation is in Objective-C format, but the action method for Timer needs to have one and only one argument of type Timer (it's NSTimer in Objective-C.)

So, you may need to define a method matches the signature in your MapVC:

func timerFired(_ timer: Timer) {
self.busInfoObject.downloadBusData(userId: ...,
warning: {_, _, _ in
...
},
delegate: self,
completed: {
...
})
}

And change the line setting the timer to:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.timerFired(_:)), userInfo: nil, repeats: true)

Sorry, for showing you an incomplete answer, please try with filling ... in my updated code.

Unrecognized selector sent to instance with Timer

You need to declare your timer

var timer = Timer()

as a property of your GameScene

and move your timer initialization to didMove(to: or any other method that runs after your scene has finished loading.

override func didMove(to view: SKView) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateEachSecond), userInfo: nil, repeats: true)
}

Another option as suggested in comments is to check in your update method if the second changed:

var timeInterval: TimeInterval = 0
var time = 0

override func update(_ currentTime: TimeInterval) {
if timeInterval < currentTime.rounded(.down) {
time += 1
timeInterval = currentTime
updateEachSecond()
}
}

func updateEachSecond() {
print("Time:", time, terminator: "s\n")
}

Swift NSTimer unrecognized selector sent to instance timerFireMethod

You've made a simple, yet common mistake.

Your method signature should be:

func timerFire(timer: NSTimer) {}

And your timer setup should be:

NSTimer(timeInterval: 1.0, target: self, selector: "timerFire:", userInfo: nil, repeats: true)

The mistake is that you're missing the colon in the selector name. timerFire is different from timerFire:. Skip the colon and it'll look for for a method like this:

func timerFire() {}

Without the NSTimer parameter. It's best though to include the parameter, and thus the colon, so that you can confirm the timer you get is the one you expect.

The same is true for notifications. If you're using Notification Center, include the colon, and the Notification object in the method.

Unrecognized selector sent to instance with Timer

You need to declare your timer

var timer = Timer()

as a property of your GameScene

and move your timer initialization to didMove(to: or any other method that runs after your scene has finished loading.

override func didMove(to view: SKView) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateEachSecond), userInfo: nil, repeats: true)
}

Another option as suggested in comments is to check in your update method if the second changed:

var timeInterval: TimeInterval = 0
var time = 0

override func update(_ currentTime: TimeInterval) {
if timeInterval < currentTime.rounded(.down) {
time += 1
timeInterval = currentTime
updateEachSecond()
}
}

func updateEachSecond() {
print("Time:", time, terminator: "s\n")
}

Terminating With Uncaught Exception Of Type NSException Timer Swift Crash

You are passing the wrong target to the timer. You want to pass clickClass, not self. And the selector should not reference the variable, it should reference the class.

timer = Timer.scheduledTimer(timeInterval: 1, target: clickClass, selector: #selector(playSound.repeatSound), userInfo: nil, repeats: true)

You should also take care to name things properly. Class, struct, and enum names should start with uppercase letters. Variable, function, and case names should start with lowercase letters.



Related Topics



Leave a reply



Submit