关于 performance-audit
面向 SwiftUI 开发者的性能审查技能,先从视图代码和数据流排查常见瓶颈,再在需要时引导使用 Instruments 分析,并提出修复与复测方法。
主要功能
- 01检查状态变更导致的视图刷新
- 02排查列表身份不稳定问题
- 03发现 body 中的高开销工作
- 04指导使用 Instruments 分析
- 05提供修复建议与基线复测
适用场景
- 01定位滚动或导航中的卡顿原因
- 02审查 SwiftUI 视图代码和数据流
- 03依据性能采样验证优化效果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ patrickserrano/ skills --skill performance-audit安装后在 Claude Code 中输入/performance-audit确认已加载。
详细介绍
SwiftUI 性能审计
概述
从埋点和基线测试,到根因分析和具体的修复步骤,对 SwiftUI 视图性能进行审计。
工作流决策树
- 如果用户提供了代码:从代码优先审查开始
- 如果用户只描述了症状:请求提供最少量的代码和上下文,然后进行代码优先审查
- 如果代码审查无法得出结论:引导用户使用 Instruments 进行性能分析
1. 代码优先审查
收集
- 目标视图/功能代码
- 数据流:状态、环境、可观察模型
- 症状和复现步骤
重点检查
- 广泛的状态变更引发的视图失效风暴
- 列表中的身份不稳定(
id频繁变化、每次渲染都调用UUID()) body中的繁重工作(格式化、排序、图像解码)- 布局抖动(嵌套层级过深、
GeometryReader、偏好值链) - 未进行降采样的大尺寸图像
- 动画过多的视图层级(大型视图树上的隐式动画)
提供
- 可能的根因及相关代码位置
- 建议的修复方案和重构方法
- 必要时提供最小复现示例或埋点建议
2. 引导用户进行性能分析
如果代码审查无法得出结论,请说明如何收集数据:
- 在 Instruments 中使用 SwiftUI 模板(Release 构建)
- 复现完全相同的交互(滚动、导航、动画)
- 捕获 SwiftUI 时间线和 Time Profiler 数据
- 导出或截取相关轨道和调用树
请用户提供:
- 跟踪导出文件或截图
- 设备/OS/构建配置
3. 常见代码异味(及修复方法)
在 body 中使用开销大的格式化器
错误示例:
var body: some View {
let formatter = NumberFormatter() // Slow allocation every render
Text(formatter.string(from: value))
}
正确示例:
final class Formatters {
static let number = NumberFormatter()
}
var body: some View {
Text(Formatters.number.string(from: value))
}
执行繁重工作的计算属性
错误示例:
var filtered: [Item] {
items.filter { $0.isEnabled } // Runs every body eval
}
正确示例:
@State private var filtered: [Item] = []
.onChange(of: items) {
filtered = items.filter { $0.isEnabled }
}
在 ForEach 中排序/筛选
错误示例:
ForEach(items.sorted(by: sortRule)) { item in
Row(item)
}
正确示例:
let sortedItems = items.sorted(by: sortRule) // Compute once
ForEach(sortedItems) { item in
Row(item)
}
身份不稳定
错误示例:
ForEach(items, id: \.self) { item in // \.self may not be stable
Row(item)
}
正确示例:
ForEach(items, id: \.stableID) { item in
Row(item)
}
在主线程上解码图像
错误示例:
Image(uiImage: UIImage(data: data)!)
正确示例:
// Decode/downsample off main thread, cache the result
@State private var image: UIImage?
.task {
image = await ImageLoader.load(data: data, targetSize: size)
}
可观察模型中的依赖范围过广
错误示例:
@Observable class Model {
var items: [Item] = []
}
var body: some View {
Row(isFavorite: model.items.contains(item)) // Entire array dependency
}
正确示例:
// Granular view models or per-item state to reduce update fan-out
4. 修复策略
| 问题 | 修复方法 |
|---|---|
| 状态变更影响范围过广 | 使用 @State/@Observable 将作用范围缩小到更靠近叶节点的位置 |
| 身份不稳定 | 为 ForEach 使用稳定且唯一的 ID |
body 中的繁重工作 | 预先计算、缓存,或移至 @State |
| 开销大的子树 | 使用 equatable() 或值包装器 |
| 大尺寸图像 | 渲染前进行降采样 |
| 布局复杂 | 减少嵌套,并尽可能使用固定尺寸 |
5. 验证
请用户重新执行相同的性能捕获,并与基线对比:
- CPU 使用率
- 丢帧情况
- 内存峰值
输出格式
提供:
- 指标表(如有数据,包含修复前后对比)
- 主要问题(按影响程度排序)
- 建议的修复方案及预估工作量
性能分析命令
# Build for profiling
xcodebuild -scheme MyApp -configuration Release -destination 'platform=iOS Simulator,name=iPhone 15 Pro' build
# Open in Instruments
open -a Instruments
Instruments 检查清单
- 使用 Release 构建(而非 Debug)
- 选择 SwiftUI 模板
- 复现完全相同的问题交互
- 查看 SwiftUI 时间线中的
body求值情况 - 使用 Time Profiler 检查热点
- 在 Animation 时间线中记录帧率下降情况
评论
支持 Markdown