Swift:使用协议扩展结果将"无法识别的选择器发送到实例"

时间:2016-03-05 02:25:23

标签: ios swift swift2

我尝试为符合协议MyProtocol的所有UIViewControllers添加一个on-tap功能。

以下是我的表现:

import UIKit

protocol MyProtocol: class{
    var foo: String? {get set}
    func bar()
}


extension MyProtocol where Self: UIViewController {
    func bar() {
        print(foo)
    }
}


class TestViewController: UIViewController, MyProtocol{
    var foo: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        foo = "testing"
        let tapGesture = UITapGestureRecognizer(target: self, action: "bar")
}

点击屏幕时会导致以下操作:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: unrecognized selector sent to instance

我理解错误但不知道如何修复它。任何人都可以建议如何做到这一点?

2 个答案:

答案 0 :(得分:5)

问题是Objective-C对协议扩展一无所知。因此,您不能使用协议扩展将方法注入到类中,以便Objective-C的消息传递机制可以看到它。您需要在视图控制器类本身中声明bar

(我知道这正是你试图避免做的事情,但我无能为力。)

可能有一种解决方法:

override func viewDidLoad() {
    super.viewDidLoad()

    foo = "testing"
    let tapGesture = UITapGestureRecognizer(target: self, action: "baz")
    self.view.addGestureRecognizer(tapGesture)
}

func baz() {
    self.bar()
}

我们现在使用Objective-C的消息传递机制来调用baz,然后使用Swift的消息传递机制来调用bar,当然这是有效的。我意识到它并不像你想象的那样干净,但至少现在bar的实现可以存在于协议扩展中。

(当然,另一种解决方案是在协议扩展存在之前完成我们所做的事情:使所有视图控制器继承自包含bar的一些常见自定义UIViewController子类。)

答案 1 :(得分:1)

马特的回答是正确的。您可以在游乐场玩耍,看看它是如何工作的。在下一个片段中,您可以看到即使某个对象可以执行objc选择器,如果使用不当,也可能导致代码崩溃: - )

//: Playground - noun: a place where people can play
import Foundation
protocol Bar {}
extension Bar {
    func bar()->Self {
        print("bar from protocol Bar extension")
        dump(self)
        return self
    }
}
class C: NSObject {
    func dummy() {
        print("dummy from C")
        dump(self)
    }
}
extension NSObject {
    func foo()->NSObject {
        print("foo from NSObject extension")
        dump(self)
        return self
    }
}
let c = C()
c.respondsToSelector("foo")     // true
c.respondsToSelector("dummy")   // true
c.foo()
let i = c.performSelector("foo")// prints the same and DON'T crash
c.dummy()   // works
//c.performSelector("dummy")    // if uncommented then
/* prints as expected
dummy from C
▿ C #0
  - super: <__lldb_expr_517.C: 0x7fcca0c05a60>
*/
// and crash!!! because performSelector returns Unmanaged<AnyObject>!
// and this is not what we return from dummy ( Void )

let o = NSObject()
o.foo()
extension NSObject: Bar {}
// try to uncomment this extension and see which version is called in following lines
/*
extension NSObject {
    func bar() -> Self {
        print("bar implemented in NSObject extension")
        return self
    }
}
*/

c.bar() // bar protocol extension Bar is called here
o.bar() // and here

o.respondsToSelector("foo") // true
o.respondsToSelector("bar") // false as expected ( see matt's answer !! )
o.performSelector("foo")    // returns Unmanaged<AnyObject>(_value: <NSObject: 0x7fc40a541270>)

performSelector有一个很大的问题:和朋友:它只能与参数一起使用并返回作为对象的值!所以如果你有什么需要,例如布尔值或返回空格你运气不好。

我想为使用Swift和ObjectiveC混合物的每个人建议further reading