虽然我想我可以回答你的问题,但这不是你想要的答案。
TL;DR: @objc 函数目前可能不在协议扩展中。您可以创建一个基类,但这不是一个理想的解决方案。
协议扩展和 Objective-C
首先,这个问题/答案 (Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c) 似乎表明,由于协议扩展在底层调度的方式,协议扩展中声明的方法对 objc_msgSend() 函数不可见,因此不可见到 Objective-C 代码。由于您尝试在扩展中定义的方法需要对 Objective-C 可见(因此 UIKit 可以使用它),它会因为不包括 @objc 而对您大喊大叫,但是一旦您包含它,它就会大喊大叫你是因为@objc 在协议扩展中是不允许的。这可能是因为协议扩展目前对 Objective-C 不可见。
我们还可以看到,添加@objc 后的错误消息指出“@objc 只能与类成员、@objc 协议和类的具体扩展一起使用。”这不是一堂课; @objc 协议的扩展与协议定义本身(即在需求中)不同,“具体”一词表明协议扩展不算作具体的类扩展。
解决方法
不幸的是,当默认实现必须对 Objective-C 框架可见时,这几乎完全阻止了您使用协议扩展。起初,我认为您的协议扩展中可能不允许 @objc,因为 Swift 编译器无法保证符合类型的类是类(即使您已明确指定 UIViewController)。所以我对P1 提出了class 要求。这不起作用。
也许唯一的解决方法是在这里简单地使用基类而不是协议,但这显然并不完全理想,因为一个类可能只有一个基类但符合多个协议。
如果您选择走这条路,请考虑这个问题(Swift 3 ObjC Optional Protocol Method Not Called in Subclass)。似乎 Swift 3 中的另一个当前问题是子类不会自动继承其超类的可选协议要求实现。这些问题的答案使用@objc 的特殊改编来解决它。
报告问题
我认为 Swift 开源项目的工作人员已经在讨论这个问题,但您可以通过使用 Apple's Bug Reporter(可能最终会进入 Swift 核心团队)或 @ 来确保他们知道987654324@。但是,其中任何一个都可能会发现您的错误过于广泛或已知。 Swift 团队也可能会考虑您正在寻找的新语言功能,在这种情况下,您应该首先查看the mailing lists。
更新
2016 年 12 月,此问题 was reported 发给 Swift 社区。该问题仍被标记为具有中等优先级的未解决问题,但添加了以下评论:
这是有意的。没有办法将方法的实现添加到每个采用者,因为扩展可以在符合协议之后添加。不过,我想如果扩展与协议位于同一模块中,我们可以允许它。
由于您的协议与您的扩展在同一个模块中,但是,您可以在 Swift 的未来版本中执行此操作。
更新 2
2017 年 2 月,Swift 核心团队的一位成员将这个问题 was officially closed 称为“不会做”,并带有以下消息:
这是故意的:由于 Objective-C 运行时的限制,协议扩展不能引入 @objc 入口点。如果你想给 NSObject 添加 @objc 入口点,扩展 NSObject。
扩展NSObject 甚至UIViewController 不会完全实现您想要的,但不幸的是它看起来不太可能。
在(非常)长期的未来,我们可能能够完全消除对 @objc 方法的依赖,但那个时候可能不会很快到来,因为 Cocoa 框架目前不是用 Swift 编写的(并且不能直到它有一个稳定的 ABI)。
更新 3
截至 2019 年秋季,这已不再是一个问题,因为越来越多的 Apple 框架是用 Swift 编写的。例如,如果您使用SwiftUI 而不是UIKit,则您完全回避了这个问题,因为在引用SwiftUI 方法时,永远不需要@objc。
用 Swift 编写的 Apple 框架包括:
- SwiftUI
- RealityKit
- 结合
- CryptoKit
既然 Swift 分别在 Swift 5.0 和 5.1 中正式成为 ABI 和模块稳定,人们预计这种模式会随着时间的推移而持续下去。