Search code examples
swiftmacosunrecognized-selector

Swift Runtime exception: unrecognized selector


In my ViewController class, I have a function:

func updateTimes() {
  // (code)
}

I create a timer:

class ViewController: NSViewController {

  var timer = Timer.scheduledTimer(timeInterval: 5,
                                   target: self,
                                   selector: 
                                   #selector(ViewController.updateTimes),
                                   userInfo: nil,
                                   repeats: true)

The compiler is happy with this. At runtime, when the timer fires, I get an exception:

unrecognized selector sent to instance 0x6000000428b0

Am I doing something obviously wrong?


Solution

  • As I wrote as a comment on NaGib ToroNgo's answer, he has given us a nice suggestion.

    The selector may not be sent to the instance of ViewController.

    I guess the ViewController would be taking this form:

    class ViewController: UIViewController {
    
        var timer = Timer.scheduledTimer(timeInterval: 5,
                                         target: self,
                                         selector: #selector(ViewController.updateTimes),
                                         userInfo: nil,
                                         repeats: true)
    
        //...(Other property declarations or method definitions)...
    
        func updateTimes() {
            // (code)
        }
    }
    

    The variable timer is declared as an instance property, and self is used in an initial value of timer. (In some old versions of Swift, this sort of usage caused error, so I was thinking that this line exists in any of the methods.)

    In the current version of Swift (tested with Swift 3.1/Xcode 8.3.3), the code above does not cause error, but self is interpreted as a method reference of self() method declared in NSObjectProtocol. So, Selector("updateTimes") is sent to the closure representing the method reference (curried function), not to the instance of the ViewController.

    The closure does not have a method named updateTimes, which caused the exception:

    unrecognized selector sent to instance


    Move the initial value code into some instance context, and then self represents the instance of the ViewController:

    class ViewController: UIViewController {
    
        var timer: Timer? //<- Keep `timer` as an instance property, but move the initial value code into `viewDidLoad()`.
    
        //...(Other property declarations or method definitions)...
    
        override func viewDidLoad() {
            super.viewDidLoad()
            //Do initialize the timer in the instance context.
            timer = Timer.scheduledTimer(timeInterval: 5,
                                             target: self,
                                             selector: #selector(self.updateTimes),
                                             userInfo: nil,
                                             repeats: true)
            //...
        }
    
        //In Swift 3, `@objc` is not needed, just for a preparation for Swift 4
        @objc func updateTimes() {
            // (code)
        }
    }
    

    I believe this does not cause unrecognized selector exception.