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

概述:

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

编译器警告:

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

版:

  • 斯威夫特: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函数当前可能不在协议扩展中。 你可以创build一个基类,尽pipe这不是一个理想的解决scheme。

协议扩展和Objective-C

首先,这个问题/答案( 可以在Objective-c中访问协议的扩展上定义的Swift方法 )似乎表明,由于协议扩展的方式是在协议扩展之下分派的,所以协议扩展中声明的方法对objc_msgSend()function,因此对Objective-C代码不可见。 由于您在扩展中试图定义的方法需要Objective-C可见(所以UIKit可以使用它),因为它不会包含@objc ,所以会大声@objc ,但是一旦包含它,它就@objc你大喊,因为@objc协议扩展中不允许使用@objc 。 这可能是因为协议扩展目前不能被Objective-C看到。

我们还可以看到,一旦添加@objc ,错误消息就会@objc “@objc只能用于类的成员,@objc协议和类的具体扩展名”。 这不是一个阶级; 对@objc协议的扩展与协议定义本身(即在需求中)是不一样的,而“具体”一词则表明协议扩展不会被视为具体的类扩展。

解决方法

不幸的是,当Objective-C框架的默认实现必须可见时,这几乎完全阻止了你使用协议扩展。 起初,我想可能@objc是不允许在你的协议扩展中,因为Swift编译器不能保证符合types是类(即使你已经明确指定了UIViewController )。 所以我把P1要求放在了上面。 这没有奏效。

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

如果你select走这条路线,请考虑这个问题( Swift 3 ObjC可选协议方法不在子类中调用 )。 Swift 3中的另一个当前问题是,子类不会自动inheritance其超类的可选协议需求实现。 这个问题的答案使用@objc的特殊适应来解决它。

报告问题

我认为已经在Swift开源项目中讨论过这个问题,但是你可以肯定,他们知道通过使用苹果的Bug Reporter ,这个Bug Reporter可能最终会进入Swift Core团队,或者Swift的bug报告者 。 但是,其中的任何一个都可能会发现您的错误太广泛或已知。 Swift团队也可能会考虑你想要成为一种新的语言function,在这种情况下,你应该首先检查邮件列表 。

更新

在2016年12月,这个问题被报告给Swift社区。 这个问题仍然以中等优先级开放,但增加了以下评论:

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

由于你的协议和你的扩展名在同一个模块中,所以你可以在未来版本的Swift中做到这一点。

更新2

2017年2月,这个问题被 Swift Core团队的成员之一正式closures为“不会做”,并发出以下消息:

这是有意的:由于Objective-C运行时的限制,协议扩展不能引入@objc入口点。 如果你想添加@objc入口点到NSObject,扩展NSObject。

扩展NSObject或甚至UIViewController不会完成你想要的东西,但不幸的是,它看起来不会成为可能。

在(非常)长远的将来,我们可能完全消除对@objc方法的依赖,但是由于Cocoa框架目前不是在Swift中编写的,因此这个时间很可能不会很快到来(并且不能直到它有一个稳定ABI)。