Swift 运行时异常:无法识别的选择器

Swift Runtime exception: unrecognized selector

在我的ViewControllerclass中,我有一个函数:

func updateTimes() {
  // (code)
}

我创建了一个计时器:

class ViewController: NSViewController {

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

编译器对此很满意。在运行时,当计时器触发时,我得到一个异常:

无法识别的选择器发送到实例 0x6000000428b0

我做错了什么吗?

您提供的代码看起来很完美。我认为问题在于,您的视图控制器以某种方式被释放或具有悬挂指针。

正如我在评论 NaGib ToroNgo 的回答时所写的那样,他给了我们一个很好的建议。

选择器可能不会发送到 ViewController 的实例。

我猜 ViewController 会采用这种形式:

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

变量timer被声明为实例属性,self用于初始值timer。 (在某些旧版本的Swift中,这种用法导致错误,所以我认为这行存在于任何方法中。)

在目前的Swift版本中(用Swift3.1/Xcode8.3.3测试),上面的代码不会报错,但是self是解释为在 NSObjectProtocol 中声明的 self() 方法的方法引用。因此,Selector("updateTimes") 被发送到表示方法引用(curried 函数)的闭包,而不是 ViewController.

的实例

闭包没有名为updateTimes的方法,导致异常:

unrecognized selector sent to instance


将初始值代码移动到一些实例上下文中,然后self表示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)
    }
}

我相信这不会导致 unrecognized selector 异常。

是时候和选择器说再见了!!!使用下面的代码

 Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { (timer) in
        // check self for nil before using
    }