== 和 === 在 Swift 中:有什么区别及底层原理

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么 Swift 中有两个等号,它们隐藏了什么?

\n\n

你是否曾在编写 Swift 代码时,突然遇到一个奇怪的运算符 ===?第一反应是:“拼写错误?还是某种超级等号?”实际上,这并非拼写错误,而是一种强大的机制,它从根本上改变了比较的逻辑。如果你在处理类、结构体或闭包,理解 == 和 === 之间的区别并非理论问题,而是实际需求。否则,你可能会遇到难以排查的 bug。

\n\n

在本文中,我们将深入 Swift 的“底层”,剖析这两个运算符的工作原理,并学会为每个任务选择正确的工具。准备好了吗?开始吧。

\n\n

什么是相等运算符 ==?

\n\n

== 运算符是用于比较值的标准“伙伴”。它检查两个对象的内容是否相等。但有一个细节:Swift 并不知道对于你自定义的数据类型来说,“相等”意味着什么。因此,要使用 ==,你的类型必须遵循 Equatable 协议。

\n\n

以下是内置类型的使用方式:

\n\n
let a = 5\nlet b = 5\nprint(a == b) // true,因为值相同\n\nlet name1 = "Alice"\nlet name2 = "Alice"\nprint(name1 == name2) // true,字符串相同
\n\n

对于类和结构体,你需要自行实现 Equatable。例如:

\n\n
struct User {\n    let id: Int\n    let name: String\n}\n\nextension User: Equatable {\n    static func == (lhs: User, rhs: User) -> Bool {\n        return lhs.id == rhs.id && lhs.name == rhs.name\n    }\n}\n\nlet user1 = User(id: 1, name: "John")\nlet user2 = User(id: 1, name: "John")\nprint(user1 == user2) // true,内容相同
\n\n

请注意:== 比较的是值,而不是引用。对于结构体来说这很直观,但对于类来说,有趣的部分才刚刚开始。

\n\n

恒等运算符 ===:当两个对象是同一个时

\n\n

===(三个等号)运算符检查的不是内容,而是引用的恒等性。它回答的问题是:“两个变量是否指向内存中的同一个对象?”

\n\n

此运算符仅适用于引用类型:类和闭包(闭包也是引用类型)。对于结构体和枚举,它根本不存在——编译器会报错。

\n\n

请看示例:

\n\n
class Car {\n    var model: String\n    init(model: String) {\n        self.model = model\n    }\n}\n\nlet car1 = Car(model: "Tesla")\nlet car2 = Car(model: "Tesla")\nlet car3 = car1\n\nprint(car1 == car2) // false,如果未实现 Equatable\nprint(car1 === car2) // false,不同的对象\nprint(car1 === car3) // true,同一个引用
\n\n

这里 car1 和 car2 是两个内容相同但不同的对象。而 car3 只是 car1 的另一个名称。=== 清晰地表明了这一点。

\n\n

底层原理:内存与指针

\n\n

要理解区别,需要深入内存。当你创建类的实例时,Swift 会在堆(heap)中为其分配一块区域,并返回指向该区域的指针。变量存储的是指针,而不是对象本身。

\n\n
    \n
  • == 通过指针比较内容:它会调用你实现的 Equatable,该方法可以查看对象的字段。
  • \n
  • === 直接比较指针:如果两个指针指向内存中的同一个地址,结果为 true。
  • \n
\n\n

对于结构体来说更简单:它们存储在栈(stack)中,并在赋值时进行复制。因此 === 对它们没有意义——两个不同的结构体在内存中始终是不同的对象,即使它们的内容相同。

博客

书籍推荐