一、最常见的 Swift 性能瓶颈层面
1️⃣ 值类型(struct / enum)的拷贝与 Copy-on-Write
常见问题
- 大 struct 在频繁传参、返回
- Array / Dictionary 被无意中触发写时复制
- 在循环中不断修改值类型
func process(_ arr: [Int]) {
var a = arr // 可能触发 COW
a.append(1)
}
典型表现
- CPU 占用高
- 内存分配频繁
- 看起来“啥也没干,但很慢”
优化方向
- 使用
inout - 控制写操作
- 必要时改用 class
- 用
withUnsafeBufferPointer处理热点路径
2️⃣ ARC(自动引用计数)
Swift 的性能杀手之一 😅
常见问题
- 闭包捕获 self
- 大量短生命周期对象
- 在循环里反复创建/释放对象
for _ in 0..<100_000 {
let obj = MyClass()
obj.doSomething()
}
典型表现
- retain / release 占比极高
- Time Profiler 里看到一堆
swift_retain
优化方向
- 减少对象创建
- 用 struct + value semantics
- 热路径避免闭包
- 使用
[weak self](但注意逻辑)
3️⃣ 动态派发(Dynamic Dispatch)
常见问题
- protocol + existential (
any Protocol) - class +
@objc/dynamic - 使用不必要的 protocol 抽象
let service: any ServiceProtocol = Service()
service.run() // 动态派发
典型表现
- 调用栈里多层 thunk
- 单次调用慢,但调用次数极多
优化方向
- 使用泛型代替 protocol
- 避免
any Protocol - 标记
final - 用
@inlinable/@inline(__always)
4️⃣ 泛型 / 协议组合导致的间接访问
Swift 的泛型快,但编译器需要知道更多信息。
常见问题
- protocol with associated type
- protocol composition
- 高度抽象的框架层
func process<T: Collection>(_ c: T) { ... }
典型表现
- 代码体积变大
- cache miss
- 反而比直接实现慢
优化方向
- 热路径具体化类型
- 框架层抽象,业务层 concrete
- 必要时拆泛型
5️⃣ String / Unicode / Character 操作
Swift 的 String 是最“安全”,也是最贵的
常见问题
- 频繁索引
String - 使用
Character而非 UTF8 / UTF16 - 正则 + substring 滥用
for ch in str {
// 每次都是 variable-width
}
典型表现
- 看似 O(n),实际更慢
- CPU hotspot 在 String 内部
优化方向
- 用
utf8/unicodeScalars - 预处理为
[UInt8] - 减少 substring
6️⃣ Bridging(Swift ↔ Objective-C)
常见问题
String↔NSStringArray↔NSArray- 调用 ObjC API 的热路径
典型表现
_bridgeToObjectiveC_unconditionallyBridgeFromObjectiveC
优化方向
- 热路径尽量纯 Swift
- 减少来回桥接
- 批量而非单次调用
7️⃣ 算法 & 数据结构(老生常谈但最致命)
Swift 写法“看起来很优雅”,但复杂度可能炸了 💣
array.contains(x) // O(n),在循环里就是 O(n²)
优化方向
- 用 Set / Dictionary
- 提前算好索引
- 减少临时集合
二、如何定位 Swift 性能瓶颈(实战流程)
🔍 Step 1:用 Instruments,而不是“猜”
必用工具
- Time Profiler(第一优先级)
- Allocations
- Leaks
- Swift Concurrency Instrument(如果用 async/await)
重点看:
- CPU Top Functions
- retain/release
- 内存分配热点
🔍 Step 2:看“Swift 特有信号”
在 Time Profiler 里看到这些,八成是 Swift 问题:
swift_retain / swift_release_swift_dynamicCastprotocol witness tableexistential
🔍 Step 3:用编译器辅助
开启优化
- Release +
-O - 关闭 Debug 测性能(Debug 性能极不真实)
编译参数
-Xfrontend -warn-long-function-bodies=200
-Xfrontend -warn-long-expression-type-checking=200
能帮你发现:
- 过度复杂表达式
- 编译器难以内联的代码
🔍 Step 4:最小化验证(非常重要)
不要一口气“重构一大块”。
而是:
- 把热点函数复制一份
- 用不同实现对比
- 用 Instruments 验证
Swift 性能优化一定要量化。
三、一句话经验总结
Swift 的性能瓶颈
80% 在 ARC / COW / 动态派发 / String
而不是“Swift 比 C++ 慢”。