一、Swift 内存布局的三条基本规则(先背)
- 对齐(Alignment)= 最大字段对齐
- Size = 字段 size + padding(向对齐取整)
- 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 信息,内存占用显著更大。