swift - 非 -'@objc' 方法不满足 '@objc' 协议(protocol)的可选要求

标签 swift swift3 swift-protocols

概述:

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

  • 编译器警告:
    Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'
    

    版本:
  • swift :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;博士: @objc功能目前可能不在协议(protocol)扩展中。您可以改为创建基类,尽管这不是理想的解决方案。

    协议(protocol)扩展和 Objective-C

    首先,这个问题/答案( Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c )似乎表明,由于协议(protocol)扩展在后台调度的方式,协议(protocol)扩展中声明的方法对 objc_msgSend() 不可见。函数,因此对 Objective-C 代码不可见。由于您尝试在扩展中定义的方法需要对 Objective-C 可见(因此 UIKit 可以使用它),因此它会因为不包括 @objc 而对您大喊大叫。 ,但是一旦您包含它,它就会对您大喊大叫,因为 @objc不允许在协议(protocol)扩展中使用。这可能是因为协议(protocol)扩展目前对 Objective-C 不可见。

    我们还可以看到添加 @objc 后的错误信息声明“@objc 只能与类的成员、@objc 协议(protocol)和类的具体扩展一起使用。”这不是一个类; @objc 协议(protocol)的扩展与协议(protocol)定义本身(即在需求中)不同,“具体”一词表明协议(protocol)扩展不算作具体的类扩展。

    解决方法

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

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

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

    报告问题

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

    更新

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

    This is intended. There is no way to add the implementation of the method to every adopter, since the extension could be added after the conformance to the protocol. I suppose we could allow it if the extension is in the same module as the protocol, though.



    但是,由于您的协议(protocol)与您的扩展位于同一模块中,因此您可以在 future 版本的 Swift 中执行此操作。

    更新 2

    2017年2月,本期was officially closed作为 Swift 核心团队成员之一的“不会做”,并带有以下消息:

    This is intentional: protocol extensions cannot introduce @objc entry points due to limitations of the Objective-C runtime. If you want to add @objc entry points to NSObject, extend NSObject.



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

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

    更新 3

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

    用 Swift 编写的 Apple 框架包括:
  • SwiftUI
  • RealityKit
  • 联合
  • CryptoKit

  • 由于 Swift 分别从 Swift 5.0 和 5.1 开始正式 ABI 和模块稳定,人们预计这种模式会随着时间的推移而继续。

    关于swift - 非 -'@objc' 方法不满足 '@objc' 协议(protocol)的可选要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39487168/

    相关文章:

    Swift 注入(inject) Javascript 将 HTML 元素删除到 WebView 中

    ios - swift 3 : Property observer for singleton

    ios - 在 Swift 3 中覆盖 UIViewController init

    ios - swift 。公共(public)协议(protocol)中的内部类型

    swift - 使用通用约束

    ios - 运行自定义 shell 脚本 '[cp] embed pods frameworks' 没有那个文件或目录

    arrays - 为什么数组不符合 Equatable,而它的项在 Swift 中是 Equatable 的?

    ios - UITextView 从底部开始

    json - swift 3 将 json 参数发布到 api

    ios - Swift - 调用自定义委托(delegate)方法