7-6.【高级特性】class、struct、enum 的内存占用和对齐规则是什么?

16 阅读5分钟

一、Swift 内存布局的三条基本规则(先背)

  1. 对齐(Alignment)= 最大字段对齐
  2. Size = 字段 size + padding(向对齐取整)
  3. Stride = size 向 alignment 对齐后的结果

Swift 的 MemoryLayout<T> 就是这三件事。

MemoryLayout<T>.size
MemoryLayout<T>.alignment
MemoryLayout<T>.stride

二、struct 的内存占用与对齐

1️⃣ struct 的布局规则

  • 字段 按声明顺序排列
  • 不重排字段(和 C 不同)
  • 每个字段起始地址必须满足自身对齐
  • struct 整体对齐 = 最大字段对齐

2️⃣ 示例 1:padding 产生的原因

struct S {
    let a: Int8   // 1 byte
    let b: Int64  // 8 bytes
}

内存布局:

offset 0: a (1)
offset 1~7: padding
offset 8~15: b
  • size = 16
  • alignment = 8
  • stride = 16

3️⃣ 字段顺序影响内存

struct S2 {
    let b: Int64
    let a: Int8
}
offset 0~7: b
offset 8:   a
offset 9~15: padding
  • size = 16
  • 如果后面再接数组,会更省空间

👉 结构体字段顺序会影响 padding,进而影响 cache 友好性


三、enum 的内存占用与对齐(最容易被低估)

1️⃣ enum 的本质

enum =

payload(最大 case) + tag(区分 case)

enum E {
    case a(Int8)
    case b(Int64)
}

2️⃣ 内存布局规则

  • payload 大小 = 最大关联值 size

  • alignment = payload 的最大 alignment

  • tag:

    • 编译器尽量塞进 payload 的空闲位
    • 塞不下才额外占字节

3️⃣ 示例

enum E {
    case small(Int8)
    case big(Int64)
}
  • payload = Int64 (8 bytes)
  • tag 可利用 Int64 的空闲高位
  • 👉 size = 8,alignment = 8

但:

enum F {
    case a(Int64)
    case b(Int64)
}
  • payload = 8
  • tag 仍可能需要额外空间
  • size 仍然通常是 8(ABI 优化)

⚠️ enum 的布局是 最复杂、最依赖编译器优化的


四、class 的内存占用与对齐

1️⃣ class 对象的整体结构(堆上)

class C {
    var x: Int
    var y: Int8
}

对象内存大致是:

[ metadata / isa ]
[ refcount / runtime info ]
[ x ]
[ padding ]
[ y ]
[ padding ]

2️⃣ 关键点

  • 对象头(runtime header):

    • 指针大小(8 bytes)
    • ARC 管理信息
  • 实例属性按 struct 规则布局

  • 对齐通常是 8 或 16 字节

👉 class 的最小占用远大于 struct


3️⃣ 示例对比

struct S {
    let x: Int
}

≈ 8 bytes

class C {
    let x: Int
}

32~40 bytes(平台相关)

即使只有一个 Int,class 也很“重”


五、Stride 的真正含义(高频追问)

struct P {
    let a: Int8
}
  • size = 1
  • alignment = 1
  • stride = 1
struct Q {
    let a: Int8
    let b: Int64
}
  • size = 16
  • alignment = 8
  • stride = 16

📌 数组中元素之间的间距 = stride


六、一张总表(面试直接用)

类型对齐规则内存占用特点
struct最大字段对齐padding 受字段顺序影响
enum最大 payload 对齐payload + tag(高度优化)
class对象头 + 属性对齐堆分配,最重

七、终极面试总结(建议原话)

struct 按字段顺序布局,对齐取最大字段;
enum 是最大 payload 加 tag,并尽量压缩;
class 在堆上,包含对象头和 ARC 信息,内存占用显著更大。

英文版

7-6. [Advanced] What are the Memory Occupancy and Alignment Rules for Class, Struct, and Enum?

I. The Three Fundamental Rules of Swift Memory Layout (Memorize These)

  1. Alignment = The alignment of the largest field within the type.
  2. Size = The sum of field sizes + padding (rounded to meet internal alignment).
  3. Stride = The distance between two instances of the type in an array (Size rounded up to the nearest multiple of Alignment).

Swift’s MemoryLayout<T> provides these three metrics.

Swift

MemoryLayout<T>.size      // Actual contiguous bytes used
MemoryLayout<T>.alignment // Addressing requirement (e.g., 8-byte boundary)
MemoryLayout<T>.stride    // Step-size in an array

II. Struct: Memory Occupancy and Alignment

1️⃣ Layout Rules

  • Fields are laid out in the order they are declared.
  • No field reordering (unlike some optimizations in other languages).
  • Each field's starting address must satisfy its own alignment.
  • The struct's overall alignment = The largest alignment among its fields.

2️⃣ Example 1: Why Padding Occurs

Swift

struct S {
    let a: Int8  // 1 byte, alignment 1
    let b: Int64 // 8 bytes, alignment 8
}

Memory Layout:

  • Offset 0: a (1 byte)
  • Offset 1~7: Padding (to align the next 8-byte field)
  • Offset 8~15: b (8 bytes)
  • Size: 16 | Alignment: 8 | Stride: 16

3️⃣ Field Order Affects Memory

Swift

struct S2 {
    let b: Int64 // 8 bytes
    let a: Int8  // 1 byte
}

Memory Layout:

  • Offset 0~7: b
  • Offset 8: a
  • Offset 9~15: Tail Padding (to satisfy the 8-byte alignment for the next element in an array)
  • Size: 9 | Alignment: 8 | Stride: 16

👉 Field order impacts padding, which in turn affects cache-friendliness and total memory footprint.


III. Enum: Memory Occupancy and Alignment (Often Underestimated)

1️⃣ The Essence of an Enum

An Enum = Payload (Largest Case) + Tag (To distinguish cases) .

2️⃣ Layout Rules

  • Payload Size = The size of the largest associated value.

  • Alignment = The maximum alignment of any payload.

  • Tag:

    • The compiler attempts to nest the tag into spare bits (extra inhabitants) of the payload.
    • If the payload has no spare bits, an extra byte is appended.

3️⃣ Example

Swift

enum E {
    case small(Int8)
    case big(Int64)
}
  • Payload: Int64 (8 bytes)
  • Tag: Can utilize the spare high-bits of the Int64.
  • Size: 8 | Alignment: 8

⚠️ Enum layout is highly complex and heavily dependent on ABI (Application Binary Interface) optimizations.


IV. Class: Memory Occupancy and Alignment

1️⃣ The Class Object Structure (On the Heap)

Swift

class C {
    var x: Int
    var y: Int8
}

The heap memory roughly looks like:

  • [ Metadata / isa pointer ] (8 bytes)
  • [ Refcount / Runtime Info ] (8 bytes)
  • [ x ] (8 bytes)
  • [ y ] (1 byte)
  • [ Padding ] (7 bytes)

2️⃣ Key Points

  • Runtime Header: Includes the pointer (8 bytes) and ARC management info.
  • Instance Properties: Laid out according to the same rules as structs.
  • Alignment: Usually 8 or 16 bytes depending on the platform.

👉 The overhead of a class is significantly higher than a struct.

3️⃣ Comparison Example

  • Struct with one Int: ≈ 8 bytes.
  • Class with one Int: ≈ 32–40 bytes (Platform dependent).

Even with a single field, a class is "heavy" due to its metadata and heap management.


V. The True Meaning of Stride (Common Interview Follow-up)

Swift

struct P { let a: Int8 } // Size: 1, Alignment: 1, Stride: 1

struct Q { 
    let a: Int8 
    let b: Int64 
} // Size: 16, Alignment: 8, Stride: 16

📌 Stride = The distance between elements in an array. If you have [Q], the CPU jumps 16 bytes to find the next a, even though the data only technically occupies 9 bytes of "meaningful" space (plus internal padding).


VI. Summary Table

TypeAlignment RuleOccupancy Characteristics
StructMax field alignmentPadding is sensitive to field order.
EnumMax payload alignmentPayload + Tag (Highly optimized/compressed).
ClassHeader + Property alignmentHeap allocated; contains metadata/ARC (Heavy).

VII. Ultimate Interview Summary

Structs are laid out by field order, aligned to the largest field. Enums consist of the largest payload plus a tag, which the compiler tries to compress. Classes reside on the heap and include an object header and ARC information, making their memory footprint significantly larger than value types.