一尘不染

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

swift

概述:

  • 我有一个协议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 {

}

阅读 572

收藏
2020-07-07

共1个答案

一尘不染

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

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可选协议方法。看来,Swift3中的另一个当前问题是子类不会自动继承其超类的可选协议要求实现。这些问题的答案使用特殊的改编@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开始),人们可能会希望这种模式随着时间的推移而继续下去。

2020-07-07