Swift:使用协议扩展导致“无法识别的select器发送到实例”

我正在尝试在所有符合协议MyProtocol UIViewController上添加一个on-tapfunction。

下面是我如何做到这一点:

 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

我了解错误,但不知道如何解决。 任何人都可以build议如何做到这一点?

问题是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 ,当然这也是可行的。 我意识到它并不像你想的那么干净,但至less现在bar的实现可以在协议扩展中生存。

(当然,另一个解决scheme是在协议扩展存在之前做我们所做的事情:让所有的视图控制器inheritance自一些包含bar常见自定义UIViewController子类。)

马特的回答是正确的。 你可以在你的操场上玩,看看它是如何工作的。 在下面的代码片段中,你可以看到即使某些对象可以执行objcselect器,如果使用不当,也可能导致代码崩溃:-)

 //: 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和朋友有一个很大的困惑:它只能用于参数和返回对象的值! 所以如果你有什么需要例如布尔或返回void,你是不幸的。

我想build议每个使用Swift和ObjectiveC混合的人进一步阅读