表示值的模型可以先考虑struct;需要对象身份或共享对象状态时再选class。用struct包住对象,不会自动让对象独立或具备线程安全性。
原理与设计判断
结构体赋值会复制存储的值。如果属性存的是类引用,复制的是引用,指向的对象仍然共享。这与深拷贝、线程安全是不同问题。继承或Objective-C对象API需求也会影响选择。 一手资料
复现代码
实验依次复制Point的整数、Box的引用,以及持有Box的Envelope。我们分开观察修改对象属性与把副本的box换成新对象两种操作,并用precondition检查各阶段的原本和副本。
这个独立示例已于 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 语言模式在 arm64 macOS 目标环境运行。不需要网络或额外软件包。
请在下载文件的文件夹中运行。检查不通过时,precondition 会终止程序。请勿使用 -Ounchecked,因为它会移除这些检查。
swift -swift-version 6 swift-class-vs-struct.swift
struct Point { var x: Int }
final class Box {
var x: Int
init(_ x: Int) { self.x = x }
}
struct Envelope { var box: Box }
let point = Point(x: 0)
var copiedPoint = point
copiedPoint.x = 10
precondition(point.x == 0 && copiedPoint.x == 10)
let box = Box(0)
let copiedReference = box
copiedReference.x = 10
precondition(box === copiedReference && box.x == 10)
let envelope = Envelope(box: Box(0))
var copiedEnvelope = envelope
copiedEnvelope.box.x = 20
precondition(envelope.box.x == 20)
copiedEnvelope.box = Box(30)
precondition(envelope.box.x == 20 && copiedEnvelope.box.x == 30)
print("struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30")
运行结果与解读
struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30
Point分成0与10,Box引用则共享10。通过Envelope副本修改内部对象,两边都会看到20;但把副本的box换成Box(30)后,原本仍是20。属性值独立与引用对象共享必须分开理解。
适用范围与限制
这是顺序执行的复制语义实验,没有测量数据竞争或性能。如果共享可变变量或内部引用,仍须检查隔离与同步。Swift 6的Sendable检查并不等于只声明一个struct。
接下来要确认的事
先列出模型复制后哪些状态应该一起变化。如果引用共享不是预期,可考虑不可变值或明确的克隆策略。需要高效复制大型存储区时,可接着看CoW实验。
来源与验证
- The Swift Programming Language: Structures and ClassesSwift.org · 官方文档 · 核查 2026年10月23日依据: 实现契约与说明的依据。运行输出另由本文所附实验确认。

