【问题标题】:What's the difference between a protocol extended from AnyObject and a class-only protocol?从 AnyObject 扩展的协议和仅类协议之间有什么区别?
【发布时间】:2015-07-22 11:55:59
【问题描述】:

这两个声明

protocol SomeProtocol : AnyObject {
}

还有这个声明

protocol SomeProtocol : class {
}

似乎只有类才能符合这个协议(即协议的实例是对对象的引用),并且没有其他效果。

它们之间有什么区别吗?应该优先选择一个吗?如果不是,为什么有两种方法可以做同样的事情?

我使用的是最新发布的 Xcode 6.3.1。

【问题讨论】:

    标签: class swift protocols


    【解决方案1】:

    官方 Swift 开发人员 (Slava_Pestov) 在Swift forums 上回答了这个问题。总结如下:

    • 你应该使用AnyObject (protocol SomeProtocol: AnyObject)。

    • AnyObjectclass 是等价的。没有区别。

    • class 最终将被弃用。

    【讨论】:

      【解决方案2】:

      关于答案https://forums.swift.org/t/class-only-protocols-class-vs-anyobject/11507/4,此答案已弃用。这些词现在都一样了。

      已弃用

      更新:在咨询the powers that be 之后,这两个定义应该是等效的,AnyObject 被用作替代品,而 class 正在完成。未来后者将取代前者,但就目前而言,它们确实存在一些细微差别。

      区别在于@objc 声明的语义。使用AnyObject,期望符合的类可能是也可能不是正确的Objective-C 对象,但语言无论如何都会这样对待它们(因为有时您会丢失静态调度)。从中得出的结论是,您可以对待 AnyObject 等人。协议约束作为请求 @objc 成员函数的一种方式,如 STL 中 AnyObject 文档中的示例所示:

      import Foundation
      class C {
           @objc func getCValue() -> Int { return 42 }
      }
      
      // If x has a method @objc getValue()->Int, call it and
      // return the result.  Otherwise, return nil.
      func getCValue1(x: AnyObject) -> Int? {
          if let f: ()->Int = x.getCValue { // <===
              return f()
          }
          return nil
      }
       // A more idiomatic implementation using "optional chaining"
      func getCValue2(x: AnyObject) -> Int? {
          return x.getCValue?() // <===
      }
       // An implementation that assumes the required method is present
      func getCValue3(x: AnyObject) -> Int { // <===
          return x.getCValue() // x.getCValue is implicitly unwrapped. // <===
      }
      

      如果您将其更改为 class-deriving 协议,相同的示例会立即失效:

      import Foundation
      
      protocol SomeClass : class {}
      
      class C : SomeClass {
          @objc func getCValue() -> Int { return 42 }
      }
      
      // If x has a method @objc getValue()->Int, call it and
      // return the result.  Otherwise, return nil.
      func getCValue1(x: SomeClass) -> Int? {
          if let f: ()->Int = x.getCValue { // <=== SomeClass has no member 'getCValue'
              return f()
          }
          return nil
      }
      
      // A more idiomatic implementation using "optional chaining"
      func getCValue2(x: SomeClass) -> Int? {
          return x.getCValue?() // <=== SomeClass has no member 'getCValue'
      }
      
      // An implementation that assumes the required method is present
      func getCValue3(x: SomeClass) -> Int { // <===
          return x.getCValue() // <=== SomeClass has no member 'getCValue'
      }
      

      所以看起来classAnyObject 的更保守版本,当您只关心引用语义而不关心动态成员查找或Objective-C 桥接时应该使用它。

      【讨论】:

      • 首先,感谢您的回答。 (也许不应该使用“AnyClass”,因为它已经是 AnyObject.Type 的类型别名。)AnyObject 与定义为protocol MyClassProtocol : class {}MyClassProtocol 之间的区别现在很清楚了。我仍然缺少的是protocol MyObjectProtocol : AnyObject {}protocol MyClassProtocol : class {} 之间的区别。
      • 我仍在努力(这可能是我的错)找到一个具体的例子,其中protocol Protocol : AnyObject {} 不能与protocol Protocol : class {} 互换。
      • 那么你现在的结论是什么?如果我正确理解 Joe Groff,protocol SomeProtocol : AnyObject { }protocol SomeProtocol : class { } 之间根本没有 no 区别,但您声称 "... 目前,它们确实提出了一些小问题差异”。请原谅我的固执,但我仍然看不到您的代码如何证明这种差异。
      • @MartinR 嗨,我在自己尝试过这个问题后偶然发现了这篇文章。我了解: class 的用法,但我也很想知道上面提到的您的 cmets 中的区别。从那以后,您是否成功地发现了上述两者之间的任何具体差异(即“......现在,它们确实存在一些细微的差异”)?请原谅我的固执。谢谢。
      • @Unheilig 我所拥有的一切都在上面,如果要相信 Joe Groff,很快就会过时。目前,class 更为保守——我怀疑是因为它的实现不完整。以后不会了。
      【解决方案3】:

      Swift programming language guide for protocolsClass-Only Protocols 部分下。它只提到了AnyObject,但没有提到class

      您可以通过将 AnyObject 协议添加到协议的继承列表中来将协议采用限制为类类型(而不是结构或枚举)。

      protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
          // class-only protocol definition goes here
      }
      

      出于这个原因,我建议在新代码或新项目中使用AnyObject 而不是class。除此之外,我看不出它们之间有任何明显的区别。

      【讨论】:

        【解决方案4】:

        AnyObject 是所有类都隐式遵循的协议 (source)。所以我想说没有区别:你可以使用任何一个来要求类约束。

        【讨论】:

          【解决方案5】:

          从 2021 年开始,Xcode 12.5,Big Sur 操作系统:

          苹果不推荐使用class

          请改用AnyObject

          快乐编码。

          【讨论】:

            【解决方案6】:

            如果您在 Xcode 9 中为 class 打开帮助(alt-click)在一行中,例如 protocol P: class {},您将获得 typealias AnyObject

            因此,无论您将协议限制为 class 还是 AnyObject,编译的代码(在 Swift 4 中)都是相同的。

            也就是说,还有 stylefuture options 的问题——未来的 Swift 版本可能希望在某些细微的地方区别对待 classAnyObject方式,即使现在不是这样。

            (编辑:这最终发生在 Swift 5.4/Xcode 12.5 中。)

            【讨论】:

            • 值得注意的是,从 XCode 12.5 开始,如果您正在扩展使用 @objc 注释的类,那么您可能会看到 Redundant constraint 'Self' : 'AnyObject' 并且需要删除 AnyObject 引用或将其替换为 NSObjectProtocol
            【解决方案7】:

            我之前说错了。 @MartinR 应该真正回答这个问题,因为他是纠正我并提供正确信息的人。

            真正的区别在于带有类限定符的协议只能应用于类,而不是结构或枚举。

            Martin,你为什么不回答,OP 可以接受你的回答?

            【讨论】:

            • 但似乎从 AnyObject 继承的协议也可以仅应用于类,而不是结构或枚举。 protocol SomeProtocol : AnyObject { } ; struct Foo : SomeProtocol { } 给出编译器错误non-class type 'Foo' cannot conform to class protocol 'SomeProtocol'。 (我感觉其实没有区别,但我无法证明。)
            • 我想这是有道理的,因为 AnyObject 也只是类对象。如果您希望结构或枚举符合该协议,则必须使用protocol SomeProtocol: Any。让我想知道类限定符的意义是什么。我(错误地)假设它将协议限制为类,而不是类的实例,但文档清楚地说明了其他情况。
            • 真正的区别是带有类限定符的协议只能应用于类,而不是结构或枚举 ?不,这没有区别,AnyObject 完全相同,因为只有对象可以符合这一点,在预期 AnyObject 的地方既不能使用枚举也不能使用结构。真正的区别在于AnyObject 可以是 Swift 类或 Obj-C 对象(或其他符合该协议的对象),但只有 Swift 类可以符合 class
            猜你喜欢
            • 1970-01-01
            • 2023-04-10
            • 1970-01-01
            • 1970-01-01
            • 2011-10-30
            • 2014-10-03
            • 1970-01-01
            • 2011-08-26
            • 2018-11-29
            相关资源
            最近更新 更多