我尝试为符合协议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
我理解错误但不知道如何修复它。任何人都可以建议如何做到这一点?
答案 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