【问题标题】:Non-'@objc' method does not satisfy optional requirement of '@objc' protocol非'@objc' 方法不满足'@objc' 协议的可选要求
【发布时间】:2017-01-22 01:36:05
【问题描述】:

概述:

  • 我有一个协议 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,我不会收到任何错误
  • 我使用的是 Xcode 8(最新公开版)。没有错误,但是会有警告

标签: swift swift3 swift-protocols


【解决方案1】:

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

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

解决方法

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

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

如果您选择走这条路,请考虑这个问题(Swift 3 ObjC Optional Protocol Method Not Called in Subclass)。似乎 Swift 3 中的另一个当前问题是子类不会自动继承其超类的可选协议要求实现。这些问题的答案使用@objc 的特殊改编来解决它。

报告问题

我认为 Swift 开源项目的工作人员已经在讨论这个问题,但您可以通过使用 Apple's Bug Reporter(可能最终会进入 Swift 核心团队)或 @ 来确保他们知道987654324@。但是,其中任何一个都可能会发现您的错误过于广泛或已知。 Swift 团队也可能会考虑您正在寻找的新语言功能,在这种情况下,您应该首先查看the mailing lists

更新

2016 年 12 月,此问题 was reported 发给 Swift 社区。该问题仍被标记为具有中等优先级的未解决问题,但添加了以下评论:

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

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

更新 2

2017 年 2 月,Swift 核心团队的一位成员将这个问题 was officially closed 称为“不会做”,并带有以下消息:

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

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

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

更新 3

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

用 Swift 编写的 Apple 框架包括:

  • SwiftUI
  • RealityKit
  • 结合
  • CryptoKit

既然 Swift 分别在 Swift 5.0 和 5.1 中正式成为 ABI 和模块稳定,人们预计这种模式会随着时间的推移而持续下去。

【讨论】:

  • @user1046037 我也这样做,因为我可以看到自己在未来的开发中多次遇到这个问题。
  • 你是对的,尽管Swift 4 目前没有其他选择,但你的答案仍然有效。
  • 我有一段时间为我工作了完全相同的代码,但在更高版本的 Xcode 中中断了。这很让人生气。 Objective-C 协议中有很多可选方法。
【解决方案2】:

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

我所拥有的是这样的:

class AwesomeClass: LessAwesomeClass {
...
}

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

扩展中的函数有这些错误:

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

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

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

【讨论】:

    【解决方案3】:

    这是另一种解决方法。我也遇到了这个问题,还不能从 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)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-03
      相关资源
      最近更新 更多