阅读 0

Swift Tips 013 - Using the === operator to compare objects by instance

代码截图

代码出处: Swift Tips 013 by John Sundell

小笔记

这段代码在说什么

在代码截图中,我们看到 Enemy 通过 InstanceEquatable 拓展遵循了 Equatable 协议并重载了 == 运算符。声明了只有内存地址相等的状态下才符合 == 的定义,此时 ===== 的含义相同。

在这个前提下,调用 contains 函数的含义就变成了判断 player 摧毁的 ememy 中,是否包含目标对象引用的内存地址,而不是与目标对象内容相同的实例。

=== 和 == 傻傻分不清楚

简单来说,Swift 中提供了两种用于判等的操作符,一个是 == ,一个是 ===

== 通常是用于判定两个对象的内容是否相同 === 通常是用于判定两个对象引用的是否为同一块内存地址。

能说的更详细一点么

对于类类型会存在多个实例指向同一个内存地址的情况,这是由于类类型本身是引用类型的缘故,类引用保存在 RTS (Run Time Stack) 上,而它们的实例保存在内存的堆上。

当我们使用 == 时,我们内心只是想验证两个实例是否相同,而不是验证两个实例是同一个实例。此时我们就需要提供一个验证两个实例相同的规则。

通常状态下,自定义类和结构体是没有默认的 ==!= 行为,我们需要让这些类型遵守 Equatable 协议并重载 static func == (lhs:, rhs:) -> Bool 函数,举个例子

class Person : Equatable {
    let ssn: Int
    let name: String

    init(ssn: Int, name: String) {
        self.ssn = ssn
        self.name = name
    }

    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.ssn == rhs.ssn
    }
}
复制代码

由于 SSN(social security number)是一个独一无二的标识符,就类似我们的身份证号,我们在判定 2 个对象是否相同时,是不需要关心名字的,不是么?

let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")

if person1 == person2 {
   print("the two instances are equal!")
}
复制代码

此时,即使 person1 和 person2 在堆上的地址不同,但由于他们的 ssn 相同,所以最终输出的还是 the two instances are equal!

if person1 === person2 {
   //It does not enter here
} else {
   print("the two instances are not identical!")
}
复制代码

=== 操作符检查的是引用的内存地址是否相同。由于 person1 和 person2 是完全 2 个独立构造的实例,所以它们在堆上的地址,也就是内存地址是不一样的,所以输出的结果一定会是 the two instances are not identical!

let person3 = person1
复制代码

正如我们所说的那样,类类型是引用类型,上面这段代码将 person1 的引用赋值给了 person3,现在它们同时指向 person1 指向的内存地址。

if person3 === person1 {
   print("the two instances are identical!")
}
复制代码

这里我想不用我太多解释,你也应该能猜到输出的结果了吧,如果还是不清楚,别担心,我们继续解释一下,这时候由于 person1 和 person3 指向的内存地址一样,也就是符号了 === 的定义,所以输出结果会是 the two instances are identical!

通过这些小例子,你弄清楚 ===== 了么?