从 AnyObject 扩展的协议和仅限于类的协议有何区别?

81

这项声明

protocol SomeProtocol : AnyObject {
}

并且这份声明

protocol SomeProtocol : class {
}

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

它们之间有什么区别吗?应该优先选择其中一个吗?如果没有,为什么有两种做同样事情的方式?

我正在使用最新发布的 Xcode 6.3.1。

7个回答

60

这是由一位官方Swift开发者(Slava_Pestov)在Swift论坛上回答的。以下是总结:

  • 你应该使用AnyObjectprotocol SomeProtocol: AnyObject)。

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

  • class最终将被弃用。


23
关于答案https://forums.swift.org/t/class-only-protocols-class-vs-anyobject/11507/4,这个答案已弃用。现在这两个词语是相同的。 弃用 更新:与有关的人员协商后,两个定义应该是等价的,使用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的协议,则会立即失败:

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桥接时,应该使用它。


1
首先,感谢您的回答。(也许不应该使用“AnyClass”,因为它已经是 AnyObject.Type 的类型别名。)现在,AnyObjectMyClassProtocol(定义为 protocol MyClassProtocol : class {})之间的区别是清楚的。我仍然不明白 protocol MyObjectProtocol : AnyObject {}protocol MyClassProtocol : class {} 之间的区别。 - Martin R
2
我仍在努力(这可能是我的错)寻找一个具体的例子,证明 protocol Protocol : AnyObject {}protocol Protocol : class {} 不可互换。 - Martin R
2
那么你现在的结论是什么?如果我正确理解Joe Groff的话,protocol SomeProtocol : AnyObject { }protocol SomeProtocol : class { }之间根本没有任何区别,但是你声称“...目前,它们确实存在一些细微的差异”。请原谅我的固执,但我仍然看不出你的代码如何展示这种差异。 - Martin R
@Unheilig:CodaFi说有一些细微的差别,所以我认为你的问题是针对他而不是我?- 我在评论中说的就是这些。 - Martin R
1
@Unheilig 我所有的东西都在那里,如果Joe Groff所说的是真的,它们很快就会过时。目前,class更为保守 - 我怀疑这是因为它的实现不完整。但它将来也会被淘汰。 - CodaFi
显示剩余2条评论

11
Swift编程语言指南的协议部分中,仅提到了AnyObject,但未提及class

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

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

因此,我建议在编写新代码或新项目时使用AnyObject而不是class。除此之外,我没有看到它们之间有任何明显的区别。


7

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

苹果已经弃用了class的使用。

请改用AnyObject

编码愉快。

Deprecated


4

AnyObject 是一个所有类都隐式符合的协议(来源)。因此,我认为它们没有区别:您可以使用任何一个来要求类约束。


3

如果你在Xcode 9中为class打开帮助(alt-click),并且在类似于protocol P: class {}的行中,你将会得到typealias AnyObject

因此,在Swift 4中编译的代码将是相同的,无论你将协议约束为class还是AnyObject

尽管如此,也有关于风格未来选项的问题——未来的Swift版本可能会以某种微妙的方式不同对待classAnyObject,即使现在不是这种情况。

(编辑:Swift 5.4/Xcode 12.5中终于出现了这种情况。)


值得注意的是,自 XCode 12.5 开始,如果您正在扩展带有 @objc 注释的类,则可能会看到“冗余约束 'Self':'AnyObject'”并且需要删除 AnyObject 引用或将其替换为 NSObjectProtocol。 - NikoRoberts

1
我之前说错了。@MartinR 应该回答这个问题,因为他纠正了我并提供了正确的信息。
真正的区别在于带有类限定符的协议只能应用于类,而不能应用于结构体或枚举。
Martin,为什么不回答,让 OP 接受你的答案呢?

6
似乎继承自 AnyObject 的协议只适用于类,而不适用于结构体或枚举。protocol SomeProtocol : AnyObject { } ; struct Foo : SomeProtocol { } 会导致编译器错误 non-class type 'Foo' cannot conform to class protocol 'SomeProtocol'。(我感觉实际上没有区别,但我无法证明。) - Martin R
1
我想这很有道理,因为 AnyObject 也只能是类对象。如果你想让结构体或枚举符合该协议,必须使用 protocol SomeProtocol: Any。这让我想知道类限定符的意义所在。我曾经错误地以为它将协议限制为类,而不是类的实例,但文档明确说明了相反的情况。 - Duncan C
真正的区别在于带有类限定符的协议只能应用于类,而不能应用于结构体或枚举。不,这不是一个区别,对于AnyObject也完全适用,因为只有对象才能符合该协议,枚举和结构体都无法在期望使用AnyObject的地方使用。真正的区别在于AnyObject可以是Swift类或Obj-C对象(或其他符合该协议的内容),但只有Swift类可以符合class - Mecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接