身份运算符还可以检查命名类型是否相同而不仅仅是引用等于

时间:2017-04-05 07:32:32

标签: swift types identity

据我所知,身份运算符用于确定两个对象是否具有相同的引用。实际上,这意味着操作员的双方都应该是一个对象。

但是,我尝试了下面的代码,它让我对身份运算符的功能感到困惑

class Dog {}

let d: Dog = Dog()

if type(of: d) === Dog.self {
    print("yep") //prints out "yep"
}

if type(of: d) == Dog.self {
    print("yep") //prints out "yep"
}

身份运算符的左侧和右侧不是对象而是类型,对于这一点,似乎语义等价运算符和对象标识运算符(看起来像)以相同的方式工作。

问题

这是一个错误还是我没有正确理解这一点。

感谢您的帮助和时间

2 个答案:

答案 0 :(得分:1)

不是错误,它们是相同的。

狗,作为一个类(类型)是一个单数,只能有一个。可以有许多实例但只有一个类。

type(of:d)返回d的所有者类,Dog.self返回Class本身。它们是完全相同的对象,单一的狗类。

答案 1 :(得分:1)

  

身份运算符的左侧和右侧不是对象而是类型。

实际上,在Apple平台上,他们对象。

这是因为Swift类在幕后实现为Objective-C类,正如Mike Ash详细介绍in this great blog post。这意味着类的元类型是一个Objective-C类,因此符合AnyObject

因此,您可以将类元类型与标识运算符进行比较,因为它定义为:

public func ===(lhs: AnyObject?, rhs: AnyObject?) -> Bool

它将比较两个对象是否是同一个对象,或者特别是在这种情况下,是相同的类元类型。

相反,在幕后,值类型的元类型不是Objective-C对象 - 它只是指向某些静态元数据的指针。如果我们重写您的示例以使用struct

struct Dog {}

let d = Dog()

// Binary operator '===' cannot be applied to two 'Dog.Type' operands
if type(of: d) === Dog.self {
    print("yep")
}

您会看到我们无法再使用===来比较元类型,因为它们不符合AnyObject。实际上,使用标识运算符来比较类元类型的能力只是它们被实现为Objective-C对象的副作用。

比较元类型的通用方法是使用等于运算符==,因为Swift专门为元类型提供了一个重载:

public func ==(t0: Any.Type?, t1: Any.Type?) -> Bool

这会检查两个元类型是否相同,但与===不同,它适用于类元类型和值类型元类型。在引擎盖下,它实现了as a simple pointer comparison,所以应始终产生与===类相同的结果。

因此,我始终建议将元类型与==进行比较,因为您不依赖于AnyObject的一致性。例如,在Linux平台上,class metatypes don't conform to AnyObject因此无法与身份运算符进行比较(尽管有趣的是,导入Foundation时,似乎为{{1}添加了===重载操作数 - 可能是为了辅助互操作性。)