协议扩展和 addTarget 因 NSInvalidArgumentException 而崩溃
Posted
技术标签:
【中文标题】协议扩展和 addTarget 因 NSInvalidArgumentException 而崩溃【英文标题】:Protocol extension and addTarget is crashing with NSInvalidArgumentException 【发布时间】:2016-02-09 12:20:37 【问题描述】:协议扩展和 addTarget 崩溃并显示消息:由于未捕获的异常 'NSInvalidArgumentException' 导致应用程序终止,原因:'-[Test.UIButton touchDown:]: unrecognized selector sent to instance 0x157eee8e0'
touchDown函数无法识别的问题在哪里?
protocol MyButtonProtocol
var holdTimer: NSTimer? get set
extension MyButtonProtocol where Self: UIButton
func enable()
addTarget(self, action: "touchDown:", forControlEvents: UIControlEvents.TouchDown)
mutating func touchDown(sender: UIButton)
print("Touch down!")
holdTimer = NSTimer(timeInterval: 1, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: true)
// Usage:
let button = UIButton()
button.enable()
【问题讨论】:
UIButton
不符合 MyButtonProtocol
?
这看起来像一个错误。也在这里:***.com/questions/31060365/… 或者它是一个功能? ***.com/questions/31431753/…
每个人都对此感到困惑。这是另一个:bugs.swift.org/browse/SR-544。因此,来自协议扩展的方法不能用于 Objective-C 调用,就像任何使用“选择器”的东西一样。感谢您的努力,Ramis,这非常有帮助。
【参考方案1】:
这一切都很奇怪。
你的代码
let button = UIButton()
button.enable()
似乎不完整,因为实例button
没有采用协议MyButtonProtocol
。
如果我写
class MyButton: UIButton, MyButtonProtocol
var holdTimer: NSTimer?
func touch3(sender: AnyObject?)
print("touch3 \(sender)")
然后
let myButton = MyButton()
myButton.enable() // this works ok
print("touch3: \(myButton.respondsToSelector(Selector("touch3:")))")
print("touchDown: \(myButton.respondsToSelector(Selector("touchDown:")))")
print("enable \(myButton.respondsToSelector(Selector("enable")))")
然后我看到输出
touch3: true
touchDown: false
enable false
所以程序成功调用了enable()
方法,但respondsToSelector
似乎没有检查协议扩展中的方法。 enable
正在工作,因为如果我将调用更改为
addTarget(self, action: "touch3:", forControlEvents: UIControlEvents.TouchDown)
那确实成功到达touch3
。
这是NSObject.respondsToSelector
实现中的错误吗?
几周前我确实注意到我无法用协议扩展中的函数覆盖super
中的函数。我以为这只是我误解的语言功能。也许这是同一问题的另一个症状?
【讨论】:
感谢您的评论。我没有运气做了更多的测试。我向苹果团队询问了这种行为。我会继续发帖的。 Apple 的回答:工程部门根据以下信息确定此问题的行为符合预期:Objective-C 无法使用 Swift 协议扩展。 Objective-C 只能使用类的扩展。我们现在关闭此错误报告。【参考方案2】:protocol MyButtonProtocol
var holdTimer: NSTimer? get set
extension MyButtonProtocol where Self: UIButton
var holdTimer: NSTimer?
get
return holdTimer
set
holdTimer = newValue;
func enable()
addTarget(self, action: "touchDown:", forControlEvents: UIControlEvents.TouchDown)
mutating func touchDown(sender: UIButton)
print("Touch down!")
holdTimer = NSTimer(timeInterval: 1, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: true)
extension UIButton: MyButtonProtocol
// Usage:
let button = UIButton()
button.enable()
注意为 UIButton 编写的扩展,这是必需的,因为您已经编写了一个协议,但 UIButton 需要实现它才能使其生效。我还在协议扩展中为holdTimer
编写了getter 和setter。您也可以在 UIButton
扩展名中编写此代码。
【讨论】:
myButton.respondsToSelector(Selector("touchDown:"))
在这个实现中仍然返回false
,并且在调用touchDown
时仍然会出现异常。【参考方案3】:
Apple Bug 报告团队的回答:
工程部门已确定此问题的行为符合预期 关于以下信息:Swift 协议扩展不是 可用于 Objective-C。只有类的扩展才能被 Objective-C。
【讨论】:
以上是关于协议扩展和 addTarget 因 NSInvalidArgumentException 而崩溃的主要内容,如果未能解决你的问题,请参考以下文章
UITapGestureRecognizer 和 addTarget 的区别
Swift:UIScrollView 和 UIControl - addTarget | addGestureRecognizer - 没有按预期工作