使用Swift 4,我试图编写一个自定义协议,提供与@objc协议的一致性。
更具体地说,我有一个自定义协议Searchable
,它提供了UIKit协议UISearchBarDelegate
的简单实现,只需要实现一个回调来处理搜索过滤器更改。
@objc protocol Searchable: class, UISearchBarDelegate {
@objc func searchFilterChanged(_: String?)
}
我在协议扩展中提供了委托实现:
extension Searchable {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchFilterChanged(searchText)
}
// .. and some other delegate methods
}
最后,在另一个扩展中,我创建并安装了搜索栏(必须从具体视图控制器的installSearchBar
调用viewDidLoad
):
extension Searchable where Self: UITableViewController {
func installSearchBar() {
let searchBar = UISearchBar()
searchBar.delegate = self
tableView?.tableHeaderView = searchBar
}
}
不幸的是,当用户在搜索栏中输入文本时,不会调用UISearchBarDelegate
的委托方法。编译器用这条消息警告我:
非 - ' @ objc'方法' searchBar(_:textDidChange :)'不满足 ' @ objc'的可选要求protocol' UISearchBarDelegate'
但是,我无法将@objc
添加到方法中,因为这会导致以下编译器错误:
@objc只能用于类成员,@ objc协议和 类的具体扩展
所以现在我的问题是:可以在Swift中实现所需的行为,还是不可能在另一个协议中实现与@objc协议的一致性?
注1:我知道这可以使用子类来实现;我还在寻找POP版本。
注2:我知道有UISearchController
也有助于简化搜索实施;我不能使用这个类,因为在我的实际代码中,我有一个更复杂的过滤器视图和其他控件 - 这就是我使用裸UISearchBar
的原因。
答案 0 :(得分:5)
这是因为Obj-C运行时使用动态调度(动态查找/消息传递)来符合对象,以确定它是否确认了所需的方法并调用它。
默认情况下,Swift协议扩展使用静态分派。从某种意义上说,当UISearchBarDelegate将消息传递给符合此协议的类时,它无法找到扩展方法,因为这对协议是静态的。因此,运行时既未检测到您的实现,也未运行。
这是一个非常深刻的话题。我不希望你用我简单的段落清楚。但是,当Swift协议扩展是新的(尝试概括UITableViewDelegate和DataSource)时,我尝试了这一点,但都是徒劳的。我必须研究为什么,我可以从我的经验和研究中告诉你,这与Objc消息传递有关,依赖于静态调度的动态调度和协议扩展。