SwiftQA Random

How does Swift handle memory for value types internally?

The starting point: value semantics ≠ literal copying

Swift’s contract is that value types (struct, enum, tuples, and things like Array/Dictionary/String) have value semantics — assigning or passing one gives you a logically independent copy. But the compiler doesn’t naively memcpy the whole thing on every assignment; what actually happens depends on the type’s storage.

1. Stack vs. heap allocation

Simple value types with fixed, known size (an Int, a struct Point { var x, y: Double }) live on the stack when possible — no heap allocation, no reference counting, no ARC traffic.

struct Point { var x: Double; var y: Double }
var p1 = Point(x: 0, y: 0)
var p2 = p1        // full stack copy, no heap involved
p2.x = 5           // p1 is untouched

2. Copy-on-write (COW) for types with heap-backed storage

For Array, Dictionary, Set, String — the struct itself is small, and the actual buffer lives on the heap, managed via COW.

var a = [1, 2, 3]
var b = a          // no copy yet — both point to the same heap buffer, refcount 2
b.append(4)         // mutation triggers a uniqueness check via isKnownUniquelyReferenced
                    // if refcount > 1: allocate new buffer, copy, then mutate

Reads never trigger a copy, only writes do — hence “copy-on-write.”

3. String’s specific twist — small string optimization

Short strings (up to 15 bytes on 64-bit) are stored inline in the struct itself, no heap allocation at all (SSO). Only strings exceeding that threshold get a heap-allocated COW buffer.

4. Value types inside classes

If a struct is a stored property of a class, its storage lives inline inside the class instance’s heap allocation.

5. Existentials and generics — where it gets less free

When a value type is used as an existential (Any, any Shape), and its size exceeds 3 words (24 bytes on 64-bit), Swift heap-allocates a box to hold it. Generic functions operating on unspecialized type parameters may box values similarly unless the compiler can specialize.

6. ARC still applies, just less often

Value types themselves aren’t reference-counted, but if a value type contains a class reference (or a COW buffer reference), copying that value type still triggers retain/release traffic on that contained reference.

Interview-ready summary table

ScenarioStorageCopy cost
Simple struct (fixed size, no heap members)Stack / inlineFull bitwise copy, cheap, no ARC
Array/Dictionary/SetStruct wrapper + heap bufferPointer copy + retain; real copy deferred until mutation (COW)
Short String (≤15 bytes)Inline in structBitwise copy, no heap
Long StringHeap buffer, COWSame as Array
Struct as class propertyInline inside class’s heap blockCopied as part of copying the containing bytes
Large struct in any Protocol / AnyHeap-boxed existential containerHeap allocation on assignment

One-liner if pressed: “Value types copy their value semantics, not necessarily their bytes — small fixed-size structs live on the stack and copy for free, while types like Array and String defer the actual copy via copy-on-write until a mutation happens on a shared buffer, and only fall back to heap allocation for large structs stored as existentials.”