你创建一个 Swift 类,复制一份,修改副本里的某个值——然后发现原对象也变了。第一次遇到这个现象时,多数人都会愣一下:这叫复制?

别急,这还真不是 Bug。Class 在 Swift 里的行为就是如此,而且这种设计是刻意为之。

打开网易新闻 查看精彩图片

我们来建一个最简单的忍者角色类:

class Ninja {var name: Stringvar powerLevel: Intinit(name: String, powerLevel: Int) {self.name = nameself.powerLevel = powerLevel

然后生成一个鸣人实例,再把它“拷贝”给第二个变量:

var naruto = Ninja(name: "Naruto", powerLevel: 9000)var naruto2 = naruto

现在只改 naruto2 的名字,换成佐助:

naruto2.name = "Sasuke"print(naruto.name)  // 输出 "Sasuke"print(naruto2.name) // 输出 "Sasuke"

明明只动了 naruto2,naruto 的名字也跟着变了。这就是 Class 和 Struct 最核心的区别。

Struct 复制时,数据直接保存在变量里,拷贝一份就像影印一份档——两张纸完全独立,改哪一张都不影响另一张。
但 Class 不一样。变量里存的并不是数据本身,而是一个指向内存堆中数据的“路标”。当你把 naruto 赋值给 naruto2,复制的只是这个路标,数据还是同一份。于是两个变量像两个路牌,同时指向同一个地址。

所以当你跟着 naruto2 的路标改掉 name,naruto 的路标指向的也是这同一块数据,自然就跟着改了。数据只有一份,两根指针只是不同的入口。

这并非设计上的疏漏。假设你正在写一款 iOS 应用,用户头像昵称同时出现在设置页、首页和通知栏三个地方。要是它们各自存一份独立拷贝,用户改一次昵称,你就得连追三个页面的数据去更新,漏掉任何一个都会出现不一致。
但用 Class,三个页面各拿着自己那根路标,指向的却是同一个用户数据。昵称改一次,所有页面自动刷新——这种引用特性,正是处理共享状态时最想要的效果。