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


103

概述:

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

编译器警告:

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 {

}

您是否拥有最新版本的Xcode?如果删除,我不会收到任何错误@objc
-Qbyte

我正在使用Xcode 8(最新的公共版本)。没有错误,但是会有警告
user1046037

Answers:


182

虽然我认为我可以回答您的问题,但这不是您想要的答案。

TL; DR: @objc功能可能当前不在协议扩展中。您可以创建一个基类,尽管这不是理想的解决方案。

协议扩展和Objective-C

首先,这个问题/答案(可以在Objective-c中访问的协议扩展上定义了Swift方法)似乎表明,由于协议扩展是在后台分发的,该objc_msgSend()函数中看不到协议扩展中声明的方法,并且因此对Objective-C代码不可见。由于您要在扩展中定义的方法需要对Objective-C可见(因此UIKit可以使用),因此对您大吼大叫不包括@objc,但是一旦您将其包括@objc进来,它便对您大吼,因为不允许协议扩展。这可能是因为协议扩展当前对Objective-C不可见。

我们还可以看到,一旦添加了@objc状态“ @objc只能与类的成员,@ objc协议和类的具体扩展一起使用” ,该错误消息就会出现。这不是一堂课。@objc协议的扩展名与协议定义本身(即在要求中)不同,并且单词“ concrete”将暗示协议扩展名不算作具体的类扩展名。

解决方法

不幸的是,当默认实现必须对Objective-C框架可见时,这几乎完全阻止您使用协议扩展。刚开始,我认为@objc您的协议扩展中可能不允许这样做,因为Swift编译器无法保证符合类型的是类(即使您已明确指定UIViewController)。因此,我对提出了class要求P1。这没有用。

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

如果您选择走这条路线,请考虑以下问题(在子类中未调用Swift 3 ObjC可选协议方法)。看来,Swift 3中的另一个当前问题是子类不会自动继承其超类的可选协议要求实现。该问题的答案使用特殊的改编@objc来解决。

报告问题

我认为这已经在Swift开源项目的开发人员中进行了讨论,但是您可以肯定使用Apple的Bug Reporter(可能最终会进入Swift核心团队)或Swift的Bug记者了解他们的情况。但是,这两种方法都可能会发现您的错误范围太广或已知。Swift团队可能还会将您正在寻找的内容视为一项新的语言功能,在这种情况下,您应该先查看邮件列表

更新资料

2016年12月,此问题已报告给Swift社区。该问题仍被标记为具有中等优先级的未解决问题,但是添加了以下注释:

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

由于您的协议与扩展名在同一个模块中,因此您可以在以后的Swift版本中执行此操作。

更新2

2017年2月,Swift核心团队的成员之一以“ Wo n't Do” 正式关闭了此问题并显示以下消息:

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

扩展NSObject甚至UIViewController无法完全实现您想要的功能,但是不幸的是,它看起来并没有成为可能。

在(非常长远的)将来,我们也许可以@objc完全消除对方法的依赖,但是由于Cocoa框架当前不是用Swift编写的,所以可能很快就不会出现(并且直到它具有稳定的ABI时)。 。

更新3

截至2019年秋季,这已经成为一个问题,因为越来越多的Apple框架正在用Swift编写。例如,如果您使用SwiftUI而不是UIKit,那么您将完全避开该问题,因为@objc在引用SwiftUI方法时将永远不需要。

用Swift编写的Apple框架包括:

  • SwiftUI
  • 现实套件
  • 结合
  • 加密工具

现在,随着Swift正式成为ABI和模块稳定版本(分别自Swift 5.0和5.1开始),人们可能会希望这种模式会随着时间的推移而持续下去。


1
@ user1046037我也这样做,因为我可以看到自己在未来的开发中多次遇到此问题。
马修·希曼

2
您是正确的,尽管Swift 4到目前为止没有其他选择,您的答案仍然有效。
user1046037

1
我有完全相同的代码可以工作一段时间,但后来在Xcode的更高版本中中断了。这很令人振奋。Objective-C协议中有很多可选方法。
Departamento B,

0

在我使用的快速框架中启用“模块稳定性”(打开“构建用于分发的库”)后,我才遇到这个问题。

我所拥有的是这样的:

class AwesomeClass: LessAwesomeClass {
...
}

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

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

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

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

将函数移至类而不是扩展中可以解决此问题。


0

这是另一个解决方法。我也遇到了这个问题,还不能从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)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.