Swift 与 Objective-C

Swift Copy-on-Write:用自制缓冲区观察分离时机

实现基于引用存储区的小型缓冲区,确认共享后的第一次写入会分离,而独占存储区的下一次写入不会再次分离。

2 分钟阅读
Swift Copy-on-Write:用自制缓冲区观察分离时机 封面图

Copy-on-Write是在保持值语义的同时延后复制存储区的实现策略。修改共享存储区之前先分离;如果已经独占,就直接修改。并非每个struct都会自动实现这项策略。

原理与设计判断

标准集合的值语义与自定义存储区的实现责任不同。这里用private类持有整数数组,并在修改方法中用isKnownUniquelyReferenced检查强引用是否唯一。所有修改路径都必须遵守这项规则。 一手资料

复现代码

IntBuffer赋值后,先通过identity确认两者共享同一个Storage。向副本添加3时应分离存储区,保留原来的数组。接着添加4时,副本已独占Storage,因此不应再次分离。

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

下载完整 Swift 文件

请在下载文件的文件夹中运行。检查不通过时,precondition 会终止程序。请勿使用 -Ounchecked,因为它会移除这些检查。

swift -swift-version 6 swift-copy-on-write-prototype-pattern.swift
struct IntBuffer {
    private final class Storage {
        var values: [Int]
        init(_ values: [Int]) { self.values = values }
    }
    private var storage: Storage
    private(set) var detachments = 0
    init(_ values: [Int]) { storage = Storage(values) }
    var values: [Int] { storage.values }
    func sharesStorage(with other: IntBuffer) -> Bool {
        storage === other.storage
    }
    mutating func append(_ value: Int) {
        if !isKnownUniquelyReferenced(&storage) {
            storage = Storage(storage.values)
            detachments += 1
        }
        storage.values.append(value)
    }
}

let original = IntBuffer([1, 2])
var copy = original
precondition(copy.sharesStorage(with: original))
copy.append(3)
precondition(!copy.sharesStorage(with: original))
precondition(original.values == [1, 2] && copy.values == [1, 2, 3])
copy.append(4)
precondition(copy.detachments == 1 && copy.values == [1, 2, 3, 4])
print("shared-before=true shared-after=false detachments=1 original=[1,2] copy=[1,2,3,4]")

运行结果与解读

shared-before=true shared-after=false detachments=1 original=[1,2] copy=[1,2,3,4]

detachments=1代表自制Storage包装层分离了一次。原本保持[1,2],副本变成[1,2,3,4]。这不是底层Array缓冲区的实际分配次数,内部Array仍可能采用自己的CoW实现。

适用范围与限制

这是单一执行流程的教学示例,不是线程安全的集合。唯一性检查不是同步机制。包含类实例的数组也不会深拷贝元素对象,因此不能说值类型替代了所有原型模式的用途。

接下来要确认的事

先区分哪些数据需要独立副本、哪些可以共享。性能收益应以实际工作负载测量;没有自定义需求时优先使用标准集合。内部引用的共享方式可参考class与struct实验。

延伸实验

来源与验证