【问题标题】:Protocol extension and addTarget is crashing with NSInvalidArgumentException协议扩展和 addTarget 因 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()

【问题讨论】:

标签: ios swift


【解决方案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。

【讨论】:

    猜你喜欢
    • 2012-05-28
    • 2022-01-02
    • 2012-10-25
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多