Swift 与 Objective-C

Swift class 与 struct:复制后还有哪些内容共享

比较值、类引用与含有引用的结构体,实际确认修改的传播范围。struct不代表深拷贝,也不会自动保证线程安全。

2 分钟阅读
Swift class 与 struct:复制后还有哪些内容共享 封面图

表示值的模型可以先考虑struct;需要对象身份或共享对象状态时再选class。用struct包住对象,不会自动让对象独立或具备线程安全性。

原理与设计判断

结构体赋值会复制存储的值。如果属性存的是类引用,复制的是引用,指向的对象仍然共享。这与深拷贝、线程安全是不同问题。继承或Objective-C对象API需求也会影响选择。 一手资料

复现代码

实验依次复制Point的整数、Box的引用,以及持有Box的Envelope。我们分开观察修改对象属性与把副本的box换成新对象两种操作,并用precondition检查各阶段的原本和副本。

这个独立示例已于 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 语言模式在 arm64 macOS 目标环境运行。不需要网络或额外软件包。

下载完整 Swift 文件

请在下载文件的文件夹中运行。检查不通过时,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实验。

延伸实验

来源与验证