状况
OC项目转Swift,学习Swift中,不断挖坑填坑。这次遇到了一个常见的Crash unrecognized selector sent to instance
。代码如下:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.button.addTarget(self, action: Selector("onButtonClicked:"), for: .touchUpInside)
}
func onButtonClicked(sender: UIButton) {
sender.superview!.animateWhenClicked()
}
原因
编译的时候并没有错误,但是有个警告报出。No method declared with Objective-C selector 'onButtonClicked:'
深究一下,Selector 源自Objective-C中的 SEL 类型。在Swift中,Selector是结构体,可以使用字符串来构造。
self.button.addTarget(self, action: Selector("onButtonClicked:"), for: .touchUpInside)
但若传入的字符串没有对应的方法名,那么程序在执行时就会Crash unrecognized selector sent to instance
。本人遇到的问题也正是如此。
解决方法
修改一下函数的参数名定义,函数可以同时拥有外部参数和内部参数,名字可以不同。可以使用下划线来省略外部参数名。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.button.addTarget(self, action: Selector("onButtonClicked:"), for: .touchUpInside)
}
func onButtonClicked(_ sender: UIButton) {
sender.superview!.animateWhenClicked()
}
再次编译运行,成功调用到此函数。但此时,编译的时候会报出一个新的警告Use '#selector' instead of explicitly constructing a 'Selector'
。#selector()
的好处是不再需要使用字符串来构造,也是Swift3中的方式。再修改一下代码。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.button.addTarget(self, action: #selector(onButtonClicked(_:)), for: .touchUpInside)
}
func onButtonClicked(_ sender: UIButton) {
sender.superview!.animateWhenClicked()
}
注意:如果函数名不唯一的话,#selector中调用函数时要加上作用域
self.button.addTarget(self, action: #selector(ViewController.onButtonClicked(_:)), for: .touchUpInside)
另外这样写也没问题
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.button.addTarget(self, action: #selector(onButtonClicked(sender:)), for: .touchUpInside)
}
func onButtonClicked(sender: UIButton) {
sender.superview!.animateWhenClicked()
}
参考资料
- Swift 中的 Selector
- Swift 函数