Swift 协议实现另一个@objc 协议

Posted

技术标签:

【中文标题】Swift 协议实现另一个@objc 协议【英文标题】:Swift protocol implementing another @objc protocol 【发布时间】:2017-11-04 09:33:49 【问题描述】:

使用 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' 协议 'UISearchBarDelegate' 的可选要求

但是,我无法将@objc 添加到方法中,因为这会导致以下编译器错误:

@objc 只能与类成员、@objc 协议和 类的具体扩展

问题

所以现在我的问题是:是否可以在 Swift 中实现所需的行为,或者不可能在另一个协议中实现对 @objc 协议的一致性?

注1:我知道这可以使用子类来实现;我还在找 POP 版本。

注意 2:我知道有 UISearchController 这也有助于简化搜索的实现;我不能使用这个类,因为在我的实际代码中,我有一个带有其他控件的更复杂的过滤器视图——这就是我使用裸 UISearchBar 的原因。

【问题讨论】:

【参考方案1】:

这是因为 Obj-C 运行时使用动态分派(动态查找/消息传递)到符合对象以了解它是否确认所需的方法并调用它。

Swift 协议扩展默认使用静态调度。从某种意义上说,当 UISearchBarDelegate 向您的类传递符合此协议的消息时,它找不到扩展方法,因为这对于协议是静态的。因此,您的实现既不会被运行时检测到,也不会起作用。

这是一个非常深刻的话题。我不希望你对我的简单段落说清楚。但是,当 Swift 协议扩展是新的(试图概括 UITableViewDelegate 和 DataSource)时,我尝试了这个,但一切都是徒劳的。我不得不研究为什么,我可以根据我的经验和研究告诉你,这与依赖于动态调度和静态调度的协议扩展的 Objc 消息传递有关。

【讨论】:

以上是关于Swift 协议实现另一个@objc 协议的主要内容,如果未能解决你的问题,请参考以下文章

没有 objc 的协议中的 Swift 可选方法

@objc 协议使 swift 编译器崩溃

Xcode/Cocoapods 为啥我不能从 Pod 实现 Swift 协议?

Swift 协议扩展实现另一个具有共享关联类型的协议

Swift & ObjC 桥 - 找不到“WKNavigationDelegate”的协议声明

swift 1.2 版中的 iCarousel 错误(候选不是 Objc 但协议需要它)