介绍
# SwiftUI Performance Audit
_Attribution: 复制自 @Dimillian 的 `Dimillian/Skills` (2025-12-31)。_
## 概述
端到端审计 SwiftUI 视图性能,从插桩和基线测定到根本原因分析和具体的修复步骤。
## 工作流程决策树
- 如果用户提供了代码,则从“代码优先审查”开始。 - 如果用户仅描述了症状,请询问最少的代码/上下文,然后进行“代码优先审查”。 - 如果代码审查没有定论,则转至“引导用户进行性能分析”,并请求提供追踪记录或截图。
## 1. 代码优先审查
收集: - 目标视图/功能代码。 - 数据流:状态、环境、可观察模型。 - 症状和复现步骤。
重点关注: - 由广泛状态更改导致的视图失效风暴。 - 列表中的身份不稳定(`id` 频繁变动,每次渲染都使用 `UUID()`)。 - `body` 中的繁重工作(格式化、排序、图像解码)。 - 布局抖动(深层堆栈、`GeometryReader`、preference 链)。 - 未进行下采样或调整大小的大图。 - 过度动画化的层级(在大型树上的隐式动画)。
提供: - 可能的根本原因并附上代码引用。 - 建议的修复和重构方案。 - 如有需要,提供最小复现或插桩建议。
## 2. 引导用户进行性能分析
解释如何使用 Instruments 收集数据: - 在 Instruments 中使用 SwiftUI 模板(Release 构建)。 - 复现确切的交互(滚动、导航、动画)。 - 捕获 SwiftUI 时间轴和 Time Profiler。 - 导出或截图相关的通道和调用树。
询问: - SwiftUI 通道 + Time Profiler 调用树的追踪导出或截图。 - 设备/操作系统/构建配置。
## 3. 分析与诊断
优先排查可能的 SwiftUI 问题根源: - 由广泛状态更改导致的视图失效风暴。 - 列表中的身份不稳定(`id` 频繁变动,每次渲染都使用 `UUID()`)。 - `body` 中的繁重工作(格式化、排序、图像解码)。 - 布局抖动(深层堆栈、`GeometryReader`、preference 链)。 - 未进行下采样或调整大小的大图。 - 过度动画化的层级(在大型树上的隐式动画)。
使用来自追踪/日志的证据总结发现。
## 4. 修复
应用针对性的修复: - 缩小状态作用域(`@State`/`@Observable` 更靠近叶子视图)。 - 稳定 `ForEach` 和列表的身份。 - 将繁重工作移出 `body`(预计算、缓存、`@State`)。 - 为昂贵的子树使用 `equatable()` 或值包装器。 - 在渲染前对图像进行下采样。 - 降低布局复杂度,或在可能的情况下使用固定尺寸。
## 常见代码异味(及修复方法)
在代码审查期间查找这些模式。
### `body` 中的昂贵格式化程序
```swift var body: some View { let number = NumberFormatter() // slow allocation let measure = MeasurementFormatter() // slow allocation Text(measure.string(from: .init(value: meters, unit: .meters))) } ```
首选在模型或专用助手中缓存格式化程序:
```swift final class DistanceFormatter { static let shared = DistanceFormatter() let number = NumberFormatter() let measure = MeasurementFormatter() } ```
### 执行繁重工作的计算属性
```swift var filtered: [Item] { items.filter { $0.isEnabled } // runs on every body eval } ```
首选在更改时预计算或缓存:
```swift @State private var filtered: [Item] = [] // update filtered when inputs change ```
### `body` 或 `ForEach` 中的排序/过滤
```swift List { ForEach(items.sorted(by: sortRule)) { item in Row(item) } } ```
首选在视图更新前进行一次排序:
```swift let sortedItems = items.sorted(by: sortRule) ```
### `ForEach` 中的内联过滤
```swift ForEach(items.filter { $0.isEnabled }) { item in Row(item) } ```
首选使用具有稳定身份的预过滤集合。
### 身份不稳定
```swift ForEach(items, id: \.self) { item in Row(item) } ```
避免对非稳定值使用 `id: \.self`;请使用稳定的 ID。
### 主线程上的图像解码
```swift Image(uiImage: UIImage(data: data)!) ```
首选在主线程之外进行解码/下采样并存储结果。
### 可观察模型中的广泛依赖
```swift @Observable class Model { var items: [Item] = [] }
var body: some View { Row(isFavorite: model.items.contains(item)) } ```
首选细粒度的视图模型或单项状态以减少更新扩散。
## 5. 验证
请用户重新运行相同的捕获操作,并与基线指标进行比较。 如果提供了数据,总结差异(CPU、掉帧、内存峰值)。
## 输出
提供: - 一个简短的指标表(如有则包含修复前/后)。 - 主要问题(按影响排序)。 - 建议的修复方案及预估工作量。
## 参考资料
将 Apple 文档和 WWDC 资源添加到 `references/` 下,由用户提供。 - 使用 Instruments 优化 SwiftUI 性能:`references/optimizing-swiftui-performance-instruments.md` - 理解和改善 SwiftUI 性能:`references/understanding-improving-swiftui-performance.md` - 了解应用中的挂起:`references/understanding-hangs-in-your-app.md` - 揭秘 SwiftUI 性能 (WWDC23):`references/demystify-swiftui-performance-wwdc23.md`