如果你先学习 Objective-C 或 Java,再转向 Swift,一定会遇到一个容易卡住的地方。
那就是“如何复制对象?”这个问题。
你很容易到处寻找类似 clone() 的方法,但在 Swift 中几乎不需要这样做。
先说结论。
得益于结构体、数组等值类型,以及 Copy-on-Write(延迟复制)优化,Swift 在语言层面替代了设计模式中的“原型模式”。
无需为复制对象额外设计模式,只要赋值,就能得到安全的副本。
本文将结合示例,整理原型模式原本要解决的问题,以及 Swift 的值类型和 Copy-on-Write 如何自然地承担这一职责。
要区分复制类型,首先要以 浅拷贝 vs 深拷贝 为基础。如果还需要复制类,Swift 原型模式与 NSCopying 展示了另一种选择。
原型模式原本要解决的问题
原型模式属于 GoF 设计模式中的创建型模式。
核心很简单:不从头创建新对象,而是复制已有对象来获得新实例。
为什么需要这种方式?
在 Objective-C 和 Java 等语言中,对象大多是引用类型。
将对象赋值给变量时,复制的不是值,而是指向同一对象的地址。
因此,修改 A 时 B 也会跟着改变。
为了避免这种情况,开发者会自行创建 clone() 或 copy() 方法,让它返回“真正的副本”。
原型模式就是应对这种情况的工具。
- 对象创建成本高,但需要多个相似对象时
- 需要不修改原对象的独立副本时
- 希望由对象自身负责复制逻辑时
Swift 值类型:赋值即可完成复制
Swift 的结构体(struct)和枚举(enum),以及 Array、Dictionary、String 等标准类型,全部都是值类型。
值类型在赋值或传入函数时就会被复制。
换句话说,语言会自动为你创建副本。
看示例会清楚得多。
struct Point { var x: Int; var y: Int }
var a = Point(x: 1, y: 2)
var b = a // 此时值会被复制
b.x = 99
// a.x仍然只 1, b.x 99
b = a这一行就替代了原型模式中clone()所承担的工作。
不需要额外的复制方法,也不需要采用复制协议。
原始的 a 会受到安全保护,而 b 会成为完全独立的副本。
对象相互纠缠所导致的棘手 bug,从结构上就不可能发生。
Copy-on-Write 解决性能问题的方式
这里自然会产生一个疑问。
“每次赋值都完整复制的话,大数组不会变得太慢吗?”
这个担忧很合理。因此 Swift 使用的优化就是 Copy-on-Write,简称 CoW。
CoW 的原理如下。
赋值时先共享内部存储空间,实际数据复制则延后进行。
当两者中的任意一方尝试修改值时,才会真正执行复制。
| 时机 | 内部操作 | 成本 |
|---|---|---|
| 赋值时 | 共享存储空间(仅引用) | 非常低 |
| 仅读取时 | 持续保持共享 | 无需复制 |
| 修改值时 | 此时才实际复制 | 仅在此时发生 |
因此,开发者既能享受值类型的安全性,也无需承担不必要的复制成本。
如果只读取而不修改,复制根本不会发生。
Array、Dictionary、Set、String 等标准库类型已经内置了 CoW。
无需自行实现,语言和标准库会自动处理。
那么,还有需要使用原型模式的情况吗?
“所以在 Swift 中,原型模式已经完全过时了吗?”
也不一定。
有些情况下必须使用类(class),例如需要引用语义、需要与 Objective-C 互操作,或需要继承。
这时如果需要类实例的独立副本,仍然必须自行实现复制逻辑。
Swift 提供了 NSCopying 协议和 copy(),这基本上就是原型模式的传统形式。
可以这样区分,会更容易理解。
- 使用结构体和值类型 → 仅通过赋值即可完成复制,无需原型模式
- 必须使用类 → 必要时自行实现复制逻辑,模式在这里继续存在
因此,Swift 社区经常建议“优先考虑值类型”。
因为以值类型为默认选择,复制问题本身就消失了。
总结
如果强行把陌生语言中的设计模式搬过来,代码反而常常会变得复杂。
只要记住,在 Swift 中值类型和 Copy-on-Write 自然填补了原型模式的位置,复制相关的烦恼就会轻松许多。
下次需要复制对象时,在寻找 clone() 之前,先想想“要不要把它改成结构体?”一定会有所帮助。

