关于 swiftui-performance
swiftui-performance 是用于审查和重构 SwiftUI 代码的技能,重点分析变更如何使视图失效并触发更新。它通过检查身份、状态、依赖、布局和生命周期,帮助开发者控制渲染工作的影响范围。
主要功能
- 01分析视图身份与状态归属
- 02查找 body 中的高开销工作
- 03检查宽泛的模型依赖
- 04审查列表、布局与绘制成本
- 05评估异步生命周期与取消
- 06提出针对性重构和验证方法
适用场景
- 01审查 SwiftUI 屏幕或列表
- 02减少不必要的视图更新
- 03调查滚动或动画卡顿
- 04验证 SwiftUI 性能假设
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ livsy90/ ios-performance-agent-skills --skill swiftui-performance安装后在 Claude Code 中输入/swiftui-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/async-lifecycle-and-mainactor.mdbody-cost-and-render-models.mdclosures-bindings-and-equatable.mdidentity-and-state.mdlayout-drawing-and-animation.mdlists-pagination-and-rows.mdobservation-and-dependencies.mdprofiling-validation.md详细介绍
SwiftUI 性能
目的
使用此技能,以优先考虑性能、关注变更局部性的思路审查和重构 SwiftUI 代码。
SwiftUI 性能优化应说明哪项变更会使界面的哪一部分失效、为何这项工作会产生影响,以及如何让更新保持局部化。
范围
在以下场景中使用此技能:
- 审查 SwiftUI 屏幕、列表、行、布局、绘制、动画和生命周期
- 通过有针对性的重构减少不必要的更新或渲染工作
- 仅在用于验证 SwiftUI 性能假设时制定性能分析计划
在以下场景中不要使用此技能:
- 仅涉及 UIKit 的性能问题
- 应用启动性能问题,除非问题出在 SwiftUI 根视图或场景构建中
- 网络、后端延迟、数据库性能或缓存问题,除非它们会影响 SwiftUI 更新
- 不涉及 SwiftUI 代码路径的通用 Instruments 操作指南
- 没有明确 SwiftUI 性能风险的大范围架构改写
核心模型
先回答以下三个问题:
- 哪些内容发生了变化?
- 哪些视图依赖这项变化?
- 这项变化会引发多少界面工作?
然后检查:
- 标识:SwiftUI 是否会在更新期间保留同一个逻辑视图?
- 生命周期:状态是否由需要它的最小稳定组件持有?
- 依赖:每个视图是否只读取渲染所需的数据?
- 渲染:
body是否开销小、行为可预测,且不包含繁重的转换工作? - 生命周期:异步工作是否由
.task、.onAppear或显式操作触发,而不是从body启动?
SwiftUI 视图是对界面的值描述。不要将其视为 UIKit 风格的长生命周期对象。
审查工作流
在建议大范围改动之前,先检查最小相关代码路径。
按以下顺序检查:
- 稳定且有意设计的标识。
body中的高开销工作。- 宽泛的状态或模型读取。
- 状态的所有权和生命周期。
- 列表、行和分页结构。
- 闭包较多的输入、自定义绑定和不必要的类型擦除。
- 重复内容中的布局、绘制和动画开销。
- 异步生命周期、取消和主线程隔离工作。
- 在有助于确认时进行性能分析或使用调试探针。
优先进行有针对性的重构,而非架构改写。
证据规则
始终区分:
- 静态代码审查发现
- 可能的风险
- 假设
- 测量结果
- 用户提供的证据
- 工具生成的证据
除非用户提供了证据,或确实运行了性能分析命令,否则绝不可编造耗时数据或声称已获得性能分析结果。
推荐表述:
这个父视图读取了整个模型,因此该模型发生变化时,屏幕的大部分区域都可能失效。
避免表述:
这会耗费 500 ms。
常见风险信号
如果以下模式出现在更新或滚动路径中,请指出:
- 不稳定的 ID,例如
.id(UUID())或var id: UUID { UUID() } - 对可变集合使用基于索引的标识
- 在
body中排序、筛选、分组、格式化、解析、解码或读取数据库 - 在
ForEach中筛选,而不是预先准备可见行 - 将一个庞大的模型传给每个子视图
- 隐藏了许多状态读取的宽泛计算属性
- 在大型重复集合中使用
AnyView - 本可使用键路径绑定时,却使用自定义
Binding(get:set:) - 行视图中存储了许多非视觉闭包
- 每一行都使用
GeometryReader、偏好项、重阴影、遮罩、模糊或叠加层 - 直接从
body启动异步工作 - 在行内使用未设保护条件的
.onAppear分页触发器
重构指南
使用能够解决根本原因的最小改动:
- 保持身份稳定
- 将开销大的转换移出
body - 在重复渲染路径之外准备好可直接用于渲染的模型
- 将状态移近拥有它的组件
- 将大型视图拆分为依赖关系明确的子视图
- 简化行视图的输入和重复结构
- 无需转换时,优先使用键路径绑定,而不是自定义闭包绑定
- 仅当视觉相等性明确,且判断成本低于重新计算时,才使用
.equatable() - 当工作与变化的输入相关且应自动取消并重启时,使用
.task(id:) - 仅当确实需要更底层控制的高频路径时,才考虑使用 UIKit
不要将 .equatable()、记忆化或缓存作为一概而论的解决方案。先说明失效或渲染问题。
参考资料路由
仅在任务需要更多细节时阅读参考资料:
references/identity-and-state.md— 身份、.id、ForEach、@State、@StateObject、@ObservedObject、自有可观察模型references/observation-and-dependencies.md— Observation、ObservableObject、宽泛依赖、环境读取references/body-cost-and-render-models.md— 排序、筛选、格式化、派生数据、渲染模型references/lists-pagination-and-rows.md—List、ScrollView、LazyVStack、行视图、分页、滚动references/closures-bindings-and-equatable.md— 闭包、绑定、.equatable()、操作处理器references/layout-drawing-and-animation.md— 布局、绘制、修饰符、动画卡顿references/async-lifecycle-and-mainactor.md—.task、.onAppear、取消、MainActorreferences/profiling-validation.md— Instruments、xctrace、signposts、XCTest、MetricKit
回复格式
对于代码评审,请按以下格式回答:
- 主要问题
- 该问题在 SwiftUI 中为何重要
- 风险级别
- 建议的重构
- 有帮助时提供代码示例
- 如需确认,应测量的内容
对于性能分析请求,请按以下格式回答:
- 场景
- 假设
- 工具或探针
- 需要关注的内容
- 如何比较优化前后的结果
回答要具体。不要笼统地说“SwiftUI 很慢”。请说明具体的身份、依赖、渲染、布局、绘制、动画或生命周期问题。
最终原则
当代码能清楚地体现变化的局部性时,SwiftUI 的性能就会有所提升。