1️⃣ 传递方式优化
(1)用 inout 避免不必要拷贝
struct BigData { var array: [Int] = Array(repeating: 0, count: 1_000_000) }
func modify(_ data: inout BigData) {
data.array[0] = 1
}
var data = BigData()
modify(&data) // 不会复制整个 struct
inout直接在调用者的内存上操作。- 注意:调用者的
data会被写入,函数内修改会影响外部。
(2)使用指针或 UnsafeMutableBufferPointer
对于极端性能要求:
var data = BigData()
data.array.withUnsafeMutableBufferPointer { buffer in
for i in 0..<buffer.count {
buffer[i] += 1
}
}
- 避免多次数组索引边界检查
- 对大型数组、循环修改特别有效
(3)传引用而非值
如果 struct 太大,频繁拷贝:
class BigDataWrapper {
var data: BigData
init(_ data: BigData) { self.data = data }
}
- 将 struct 放到 class 里,通过引用传递
- CoW 原理依然生效(struct 内部 buffer 可以共享)
- 注意:class 会改变值语义,可能带来副作用
2️⃣ 减少拷贝开销
(1)拆分大型 struct
- 把大型 struct 拆成 小 struct + 指向 heap 的 buffer
- 例如:
struct BigData {
var metadata: Meta
var values: [Int] // 堆上
}
- 这样 struct 本身很小,传递时只拷贝指针和 metadata
- 实际大量数据在 heap,拷贝只在必要时触发 CoW
(2)利用 CoW 容器
- Swift 数组、字典本身是 CoW
- 不要把数组全改成 class 才能共享内存
- 频繁读取时共享 buffer,写入时才触发复制
(3)避免无用返回值
func process(_ data: BigData) -> BigData { ... }
// 返回会触发复制
- 对大型 struct,尽量用 inout 或 修改内部引用类型,避免每次返回都复制
3️⃣ 内存布局优化
- 尽量把 小字段放在前,large buffer 放在后,减少拷贝开销
- 对嵌套 struct,要留意 连续内存访问
- Swift 5+ 对 struct 优化比较好,但在堆外部 buffer 时性能依然更稳
4️⃣ 总结策略
| 场景 | 优化手段 |
|---|---|
| 频繁传递大型 struct | inout 或 class 包装 |
| 修改大型数组/子 struct | UnsafeMutableBufferPointer 或 CoW 容器 |
| 大型 struct 返回 | 尽量用引用传递,减少返回值复制 |
| 多个函数共享同一个数据 | CoW + heap buffer 或 class 共享 |
💡 核心原则:
Swift struct 值语义很强,但性能成本来自拷贝大型数据。优化的思路是:
减少拷贝次数 + 利用 heap buffer 或引用 + 避免不必要的返回。