非"@objc"方法不满足"@objc"协议的可选要求



概述:

  • 我有一个协议P1,它提供了Objective-C可选函数之一的默认实现
  • 当我提供可选函数的默认实现时,会出现警告

编译器警告:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'

版本:

  • Swift:3
  • Xcode:8(公开发布)

尝试次数:

  • 尝试添加@objc,但没有帮助

问题:

  • 如何解决此问题
  • 附近有工作吗

代码:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}

class A : UIViewController, P1 {
}

虽然我认为我可以回答你的问题,但这不是你喜欢的答案。

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只能与类的成员、@obqc协议和类的具体扩展一起使用。"这不是一个类;objc协议的扩展与协议定义本身(即在需求中)不同,"具体"一词表明协议扩展不算作具体的类扩展。

变通办法

不幸的是,当默认实现必须对Objective-C框架可见时,这几乎完全阻止了您使用协议扩展。起初,我认为@objc在您的协议扩展中可能是不允许的,因为Swift编译器不能保证一致类型是类(即使您已经特别指定了UIViewController)。所以我对P1提出了class的要求。这不起作用。

也许唯一的解决方法是简单地使用基类而不是协议,但这显然不是完全理想的,因为一个类可能只有一个基类,但符合多个协议。

如果你选择走这条路,请考虑这个问题(Swift 3 ObjC可选协议方法在子类中未调用)。Swift 3中的另一个当前问题似乎是子类不会自动继承其超类的可选协议需求实现。这些问题的答案使用了@objc的特殊改编来绕过它

报告问题

我认为Swift开源项目的工作人员已经在讨论这一点,但你可以通过使用苹果的Bug Reporter(可能最终会进入Swift核心团队)或Swift的Bug Reporter来确保他们已经意识到了这一点。然而,这两种方法中的任何一种都可能会发现你的bug过于宽泛或已经为人所知。Swift团队也可能会考虑你想要的是一个新的语言功能,在这种情况下,你应该首先查看邮件列表。

更新

2016年12月,Swift社区报道了这一问题。该问题仍然被标记为开放,具有中等优先级,但添加了以下注释:

这是有意的。没有办法向每个采用者添加该方法的实现,因为可以在符合协议之后添加扩展。不过,我想如果扩展与协议在同一个模块中,我们可以允许它。

然而,由于您的协议与扩展位于同一模块中,因此您可能能够在Swift的未来版本中做到这一点。

更新2

2017年2月,Swift核心团队的一名成员以"不会做"的名义正式关闭了这一期,并发布了以下消息:

这是有意的:由于Objective-C运行时的限制,协议扩展无法引入@objc入口点。如果要将@objc入口点添加到NSObject,请扩展NSObject。

扩展NSObject甚至UIViewController不会完全实现您想要的,但不幸的是,它看起来并不可能。

在(非常)长期的未来,我们可能能够完全消除对@objc方法的依赖,但这一时间可能不会很快到来,因为Cocoa框架目前不是用Swift编写的(在它有稳定的ABI之前是不可能的)。

更新3

截至2019年秋季,这一问题越来越小,因为越来越多的苹果框架正在用Swift编写。例如,如果使用SwiftUI而不是UIKit,则完全避开了这个问题,因为在引用SwiftUI方法时,@objc是不必要的。

用Swift编写的苹果框架包括:

  • SwiftUI
  • RealityKit
  • 联合收割机
  • CryptoKit

由于Swift分别在Swift 5.0和5.1中正式实现了ABI和模块稳定,人们预计这种模式会随着时间的推移而持续。

在我使用的一个swift框架中启用"模块稳定性"(打开"为分发构建库")后,我刚刚遇到了这个问题。

我拥有的是这样的东西:

class AwesomeClass: LessAwesomeClass {
...
}
extension AwesomeClass: GreatDelegate {
func niceDelegateFunc() {
}
}

扩展中的函数有以下错误:

  • "LessAwesomeClass"子类扩展中的"@objc"实例方法需要iOS 13.0.0

  • 非'@objc'方法'niceDegateFunc'不满足'@objc'协议'GreatDelegate'的要求

将函数移到类中而不是扩展中解决了问题。

这里有另一个解决方法。我也遇到了这个问题,还不能从UIKit切换到SwiftUI。将默认实现移动到公共基类中对我来说也不是一个选择。我的默认实现非常广泛,所以我真的不想重复所有的代码。我最终使用的解决方法是在协议中使用包装器函数,然后简单地从每个类中调用这些函数。不漂亮,但可能比其他选择更好,具体取决于情况。然后你的代码看起来像这样:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
}
}

相关内容

  • 没有找到相关文章

最新更新