Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4 Timer Crashes with NSException

I've been searching for a way to use a Timer in Swift 4 and looked at this article. I test out my code in xcode and when the the timer first ticks (in this case after 10 seconds) the app crashes and I get an error, although the build succeeded.

2017-11-20 19:54:42.781502-0700 Rock Prodigy[3022:554505] -[_SwiftValue   tick]: unrecognized selector sent to instance 0x608000051520
2017-11-20 19:54:42.791278-0700 Rock Prodigy[3022:554505] ***  Terminating app due to uncaught exception 'NSInvalidArgumentException',   reason: '-[_SwiftValue tick]: unrecognized selector sent to instance  0x608000051520'
*** First throw call stack:
(
0   CoreFoundation                      0x000000010360d1ab __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x0000000102ca2f41 objc_exception_throw + 48
2   CoreFoundation                      0x000000010368da34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3   CoreFoundation                      0x00000001035900a8 ___forwarding___ + 1432
4   CoreFoundation                      0x000000010358fa88 _CF_forwarding_prep_0 + 120
5   Foundation                          0x000000010270e1ee __NSFireTimer + 83
6   CoreFoundation                      0x000000010359d2a4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
7   CoreFoundation                      0x000000010359cf62 __CFRunLoopDoTimer + 1026
8   CoreFoundation                      0x000000010359cb1a __CFRunLoopDoTimers + 266
9   CoreFoundation                      0x0000000103594534 __CFRunLoopRun + 2308
10  CoreFoundation                      0x00000001035939b9 CFRunLoopRunSpecific + 409
11  GraphicsServices                    0x00000001090e29c6 GSEventRunModal + 62
12  UIKit                               0x0000000103a885e8 UIApplicationMain + 159
13  Rock Prodigy                        0x000000010238b637 main + 55
14  libdyld.dylib                       0x0000000107aa1d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

Here is my swift 4 code

import UIKit

class GeneralFitness: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

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

    @objc func tick() {
        print("tick")
    }


}

Let me know if you have any questions.

like image 734
Branson Camp Avatar asked Dec 08 '22 16:12

Branson Camp


1 Answers

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()
    }

}
like image 196
Rob Avatar answered Dec 19 '22 01:12

Rob