Swift 值类型和引用类型有什么区别?什么时候用 struct?
值类型(struct、enum、tuple)赋值时拷贝,每个变量拥有独立的数据副本;引用类型(class、closure)赋值时传递引用,多个变量指向同一个实例。这是 Swift 最根本的类型区分,直接影响内存管理、线程安全和代码行为。
值类型的"拷贝"不是每次赋值都真拷贝——Swift 有写时复制(Copy-on-Write)优化,只有修改时才真正创建副本,所以大数组的传参开销没有想象中大。
追问
值类型和引用类型在内存上有什么区别?
值类型通常分配在栈上,分配和释放由编译器管理,速度快。引用类型分配在堆上,由 ARC 管理生命周期,有引用计数的开销。栈上的值类型在函数返回时自动释放,不需要额外的内存管理;堆上的引用类型需要 ARC 追踪引用计数,计数归零时才释放。
写时复制(Copy-on-Write)是什么?
值类型赋值时不立即拷贝数据,而是和原始值共享底层存储。只有当某一方尝试修改时,才真正创建独立副本。Array、Dictionary、String 等标准库类型都用了这个优化。自定义 struct 要支持 COW,需要手动检测引用计数并在修改时拷贝。
什么时候必须用引用类型(class)?
三种情况:需要继承和多态、需要共享状态(多个地方修改同一个实例)、需要控制生命周期(deinit 做清理)。UI 控件的 ViewModel、网络请求的 Session、文件句柄——这些场景需要用 class。其他情况优先 struct。
值类型一定线程安全吗?
值类型在单次赋值/传参时是安全的(因为拷贝隔离),但如果你把值类型放在 class 里作为属性,多线程同时修改就不安全了——值类型的拷贝只发生在赋值时,不是每次访问时。所以"值类型线程安全"的说法不严谨,准确说是"值类型的独立副本之间互不影响"。
struct 里嵌套 class 会怎样?
struct 仍然是值类型,赋值时 struct 本身被拷贝,但内部的 class 引用不会深拷贝——多个 struct 副本共享同一个 class 实例。这会导致意外的数据共享:修改一个 struct 副本里的 class 属性,其他副本也会受影响。如果需要真正的深拷贝,必须手动实现 Clone 方法或者在 struct 里只放值类型。
写段代码
swiftstruct Point { var x: Int, y: Int } // 值类型 class Node { var value: Int = 0 } // 引用类型 var p1 = Point(x: 1, y: 2) var p2 = p1 // 拷贝 p2.x = 10 print(p1.x) // 1 — p1 不受影响 var n1 = Node() n1.value = 42 var n2 = n1 // 传递引用 n2.value = 99 print(n1.value) // 99 — n1 受影响,指向同一实例 // struct 嵌套 class 的陷阱 struct Wrapper { var node = Node() } var w1 = Wrapper() var w2 = w1 // struct 拷贝,但 node 是引用 w2.node.value = 100 print(w1.node.value) // 100 — 共享同一个 Node 实例!