Swift & Objective-C

Swift Copy-on-Write: Observe Detachment in a Custom Buffer

Build a small reference-backed buffer and verify one detachment on the first shared write, with no extra detachment on a later unique write.

2 min read
Cover image for Swift Copy-on-Write: Observe Detachment in a Custom Buffer

Copy-on-write preserves value semantics while postponing storage copying. Detach shared storage before mutation; mutate it directly when uniquely owned. This strategy is not automatically implemented by every struct.

Mechanism and design choice

A standard collection’s value semantics and a custom storage implementation are different responsibilities. Here a private class holds an integer array, and mutating methods use isKnownUniquelyReferenced to check strong-reference uniqueness. Every mutation path must preserve that discipline. Primary source

Reproduce the experiment

After assigning IntBuffer, identity checks establish that both values share one Storage object. Appending 3 to the copy must detach storage and preserve the original array. Appending 4 should not detach again because the copy already owns its Storage uniquely.

This standalone example was run on 2026-10-23 with Apple Swift 6.4 targeting arm64 macOS, in Swift 6 language mode. It needs no network or additional packages.

Download the complete Swift file

Run the command in the download directory. Preconditions stop execution if a check fails. Do not use -Ounchecked: it removes these checks.

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]")

Recorded output and interpretation

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

detachments=1 counts one separation of our Storage wrapper. The original remains [1,2] and the copy becomes [1,2,3,4]. It does not measure allocations of the underlying Array buffer, which may use its own CoW implementation.

Scope and limitations

This is a single-flow teaching example, not a thread-safe collection. A uniqueness check is not synchronization. An array of class instances does not deeply clone its elements, so value types do not eliminate every use of the prototype pattern.

What to check next

Distinguish data requiring independent copies from data allowed to remain shared. Measure performance under a real workload and prefer standard collections unless a custom type is justified. Use the class-versus-struct experiment to examine nested references.

Related experiment

Sources and verification

  • Swift Standard Library: isKnownUniquelyReferencedSwift.org · Official documentation · Checked October 23, 2026Supports: Source for the implementation contract and explanation. The output was checked separately using the experiment included here.