Mobile Development 20 min read

Unveiling Swift’s Array: Memory Allocation, SIL Insights, and Copy‑On‑Write Mechanics

This article dives deep into Swift's Array implementation, explaining how the language allocates memory, the role of SIL and internal structs like _Array, _ArrayBody, and _ContiguousArrayStorage, and how copy‑on‑write ensures efficient mutation handling, all illustrated with real code snippets and debugger output.

Sohu Smart Platform Tech Team
Sohu Smart Platform Tech Team
Sohu Smart Platform Tech Team
Unveiling Swift’s Array: Memory Allocation, SIL Insights, and Copy‑On‑Write Mechanics

Background

In a previous article I mentioned a crash caused by iterating over an Array while mutating it. The crash log shows that a mutable array was mutated during enumeration, which Swift forbids because Array is not thread‑safe.

To understand how Swift implements Array, I created a simple command‑line project and inspected the generated SIL (Swift Intermediate Language) and source code.

Array Definition

Array

in Swift is a struct. Its definition in Array.swift looks like this:

@frozen
public struct Array<Element>: Swift._DestructorSafeContainer {
    #if _runtime(_ObjC)
    @usableFromInline internal typealias _Buffer = _ArrayBuffer<Element>
    #else
    @usableFromInline internal typealias _Buffer = _ContiguousArrayBuffer<Element>
    #endif
    @usableFromInline internal var _buffer: _Buffer
    @inlinable internal init(_buffer: _Buffer) { self._buffer = _buffer }
}

The struct contains a single stored property _buffer, which is either an _ArrayBuffer or a _ContiguousArrayBuffer depending on the runtime.

Creating an Array and SIL Generation

I wrote the following Swift code:

var num: Array<Int> = [1, 2, 3]
withUnsafePointer(to: &num) {
    print($0)
}
print("end")

Compiling with

swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil

produced SIL that shows the array being created via @Swift._allocateUninitializedArray<A>(). This intrinsic returns a tuple containing the newly allocated array and a raw pointer to its storage.

Allocation Path

The allocation function calls Builtin.allocWithTailElems_1, which eventually invokes swift::swift_allocObject to reserve heap memory for a _ContiguousArrayStorage instance.

HeapObject *swift::swift_allocObject(HeapMetadata const *metadata, size_t requiredSize, size_t requiredAlignmentMask) {
    CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
}

The allocated storage is then adopted by the array via Array._adoptStorage:

internal static func _adoptStorage(_ storage: __owned _ContiguousArrayStorage<Element>, count: Int) -> (Array, UnsafeMutablePointer<Element>) {
    let innerBuffer = _ContiguousArrayBuffer<Element>(count: count, storage: storage)
    return (Array(_buffer: _Buffer(_buffer: innerBuffer, shiftedToStartIndex: 0)), innerBuffer.firstElementAddress)
}

The innerBuffer is a _ContiguousArrayBuffer that holds a reference to the storage and initializes its header:

internal init(count: Int, storage: _ContiguousArrayStorage<Element>) {
    _storage = storage
    _initStorageHeader(count: count, capacity: count)
}

Buffer Header

The header is an _ArrayBody struct containing a _SwiftArrayBodyStorage with count and _capacityAndFlags. The capacity is stored as (capacity << 1) | elementTypeIsBridgedVerbatim, so the actual capacity is retrieved by shifting right one bit.

internal init(count: Int, capacity: Int, elementTypeIsBridgedVerbatim: Bool = false) {
    _storage = _SwiftArrayBodyStorage(
        count: count,
        _capacityAndFlags: (UInt(truncatingIfNeeded: capacity) << 1) |
            (elementTypeIsBridgedVerbatim ? 1 : 0))
}

internal var capacity: Int { return Int(_capacityAndFlags >> 1) }

First Element Address

The buffer provides a pointer to the first element using the builtin projectTailElems operation:

internal var firstElementAddress: UnsafeMutablePointer<Element> {
    return UnsafeMutablePointer(Builtin.projectTailElems(_storage, Element.self))
}

This pointer points to the memory immediately after the buffer’s header, where the actual array elements reside.

Copy‑On‑Write (COW) Mechanics

Swift arrays use copy‑on‑write to avoid unnecessary copying. When a mutating operation such as append is called, the implementation first checks whether the underlying storage is uniquely referenced:

internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
    if !_buffer.beginCOWMutation() {
        _createNewBuffer(bufferIsUnique: false, minimumCapacity: count + 1, growForAppend: true)
    }
}

The uniqueness test examines the strong reference count of the storage; a count of zero means the array has the sole reference.

return !getUseSlowRC() && !getIsDeiniting() && getStrongExtraRefCount() == 0;

If the storage is shared, a new buffer is allocated, and the elements are either moved (if the original buffer was unique) or copied:

if bufferIsUnique {
    let dest = newBuffer.firstElementAddress
    dest.moveInitialize(from: mutableFirstElementAddress, count: c)
    _native.mutableCount = 0
} else {
    _copyContents(subRange: 0..<c, initializing: newBuffer.mutableFirstElementAddress)
}

Thus, the COW behavior hinges on the heap‑allocated storage’s reference count.

Verification

Running the sample code and inspecting memory with LLDB confirms that the Array variable holds a pointer to a _ContiguousArrayStorage object, and that the storage’s reference count changes as expected when the array is copied and mutated.

Conclusion

Although Array is a Swift struct, its elements live on the heap inside a _ContiguousArrayStorage. Swift’s copy‑on‑write optimization relies on the storage’s strong reference count: a count of zero indicates unique ownership, allowing in‑place mutation; otherwise a new buffer is allocated and the data is copied.

References

NSMutableArray原理揭露: http://blog.joyingx.me/2015/05/03/NSMutableArray%20%E5%8E%9F%E7%90%86%E6%8F%AD%E9%9C%B2/

探索Swift中Array的底层实现: https://juejin.cn/post/6931236309176418311

iOSmemory managementSwiftCopy-on-WriteSIL
Sohu Smart Platform Tech Team
Written by

Sohu Smart Platform Tech Team

The Sohu News app's technical sharing hub, offering deep tech analyses, the latest industry news, and fun developer anecdotes. Follow us to discover the team's daily joys.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.