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
Xcode 9: Swift Dependency Analysis Error
Non-Main Bundle File as Alert Sound
How to Pass Values from a Pop Up View Controller to the Previous View Controller
What Is the Simplest Way to Retrieve the Device Serial Number of an iOS Device Using Monotouch
How to Convert a Persian Date into a Gregorian in Swift
Avcapturevideopreviewlayer Add Overlays and Capture Photo in iOS
Rxswift - Generic Parameter 'Self' Could Not Be Inferred
Scroll All the Way Down to the Bottom of Uitableview
How to Set Up Array for Multi Annotations with Swift
How to Download and View Images from the New Firebase Storage
Cannot Access Appdelegate While Testing Xcode Project
Swift 3 - Loading Multiple Viewcontrollers at Launch
How to Handle Usernotifications Actions in iOS 10
How to Properly Handle a Nil Uiapplication.Sharedapplication().Keywindow
How to Update iOS 14 Widget Background Color from the App
iPhone Simulator Plays Video, Real Device Won'T
Records, Zone Doesn't Displayed in Dashboard and Delete Zone Issue Cloudkit