非“@obc”方法不满足“@obc”协议的可选要求

概述:

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

编译器警告:

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

版本:

  • 斯威夫特: 3分
  • Xcode: 8(公开发行版)

尝试:

  • Tried adding @objc but doesn't help

问题:

  • 我是怎么解决的?
  • 这附近有工作吗?

密码:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {


}


extension P1 where Self : UIViewController {


func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}




class A : UIViewController, P1 {


}
26824 次浏览

我想我可以回答你的问题,但你不会喜欢这个答案的。

目前协议扩展中可能没有 @objc函数。您可以创建一个基类,尽管这不是一个理想的解决方案。

协议扩展和目标 -C

首先,这个问题/答案(在 Objective-c 中定义可访问协议扩展的 Swift 方法)似乎表明,由于协议扩展的发送方式隐藏在引擎盖下,协议扩展中声明的方法对于 objc_msgSend()函数是不可见的,因此对于 Objective-C 代码是不可见的。因为你试图在扩展中定义的方法需要对 Objective-C 可见(这样 UIKit就可以使用它) ,它会因为你没有包含 @objc而对你大喊大叫,但是一旦你包含了它,它就会对你大喊大叫,因为协议扩展中不允许包含 @objc。这可能是因为目前 Objective-C 不能看到协议扩展。

我们还可以看到,一旦我们添加了 @objc,错误消息就表明“@objecc 只能用于类的成员、@objecc 协议和类的具体扩展”这不是一个类;@objecc 协议的扩展与协议定义本身(即需求)中的扩展是不一样的,“具体”这个词意味着协议扩展不能算作具体的类扩展。

解决办法

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

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

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

报告问题

我认为这已经在那些从事 Swift 开源项目的人员中讨论过了,但是你可以确定他们通过使用 苹果的 Bug 记者或者 斯威夫特的窃听记者来了解这一点,苹果的 Bug 记者很可能最终会成为 Swift 核心团队的成员。但是,这两种方法都可能会发现你的 bug 过于宽泛或者已经知道了。Swift 团队可能还会考虑您正在寻找的是一个新的语言特性,在这种情况下,您应该首先查看 邮寄名单

更新

2016年12月,本期 was reported发给斯威夫特社区。这一问题仍然处于中度优先的开放状态,但增加了以下评论意见:

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

Since your protocol is in the same module as your extension, however, you may be able to do this in a future version of Swift.

更新2

In February 2017, this issue was officially closed as "Won't Do" by one of the Swift Core Team members with the following message:

这是有意为之的: 由于 Objective-C 运行时的限制,协议扩展不能引入@objecc 入口点。如果要向 NSObject 添加@objecc 入口点,请扩展 NSObject。

延长 NSObject或甚至 UIViewController将不会完全达到你想要的,但不幸的是,它看起来不会成为可能。

In the (very) long-term future, we may be able to eliminate reliance on @objc methods entirely, but that time will likely not come anytime soon since Cocoa frameworks are not currently written in Swift (and cannot be until it has a stable ABI).

更新3

到2019年秋天,这个问题已经变得不那么严重了,因为越来越多的苹果框架都是用 Swift 编写的。例如,如果使用 SwiftUI而不是 UIKit,就完全回避了这个问题,因为在引用 SwiftUI方法时,@objc永远都不是必需的。

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

  • SwiftUI
  • 现实工具包
  • 合并
  • 加密工具包

One would expect this pattern to continue over time now that Swift is officially ABI and module stable as of Swift 5.0 and 5.1, respectively.

I just ran into this after enabling 'module stability' (turning on 'Build libraries for distribution') in a swift framework I use.

我所拥有的是这样的东西:

class AwesomeClass: LessAwesomeClass {
...
}


extension AwesomeClass: GreatDelegate {
func niceDelegateFunc() {
}
}

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

  • “ LessAwesomeClass”子类的扩展中的“@objecc”实例方法需要 iOS 13.0.0

  • 非‘@objecc’方法‘ niceGenerateFunc’不满足‘@objecc’协议‘ 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)
}
}