mcpField
S
swiftui-debugging

swiftui-debugging

github.com/mengto开发7.1k65 次安装5 天前更新
代码仓库

关于 swiftui-debugging

面向 SwiftUI 开发者的调试技能,通过性能分析、视图身份检查和轻量埋点,将性能问题转化为有证据支撑的诊断与针对性修复。

主要功能

  1. 01追踪视图 body 重新求值
  2. 02诊断视图身份变化
  3. 03检查列表与网格加载
  4. 04使用 Instruments 和日志
  5. 05实施针对性性能修复

适用场景

  1. 01排查意外的 body 更新
  2. 02解决状态重置与重复出现
  3. 03定位滚动或初始加载缓慢
  4. 04将耗时操作移出 body

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mengto/skills --skill swiftui-debugging

安装后在 Claude Code 中输入/swiftui-debugging确认已加载。

详细介绍

SwiftUI 调试

使用此技能将 SwiftUI 性能问题转化为简短的证据分析流程:找出发生了什么变化,确认 SwiftUI 是在重新创建视图还是仅重新求值,然后实施范围最小的修复。

工作流

  1. 重现或定位问题。

    • 编辑前,先搜索提到的视图、模型、列表/网格或修饰符链。
    • 只有在无法从代码库推断出目标视图或交互时,才请求最小复现示例。
    • 相较于猜测,优先查看性能分析跟踪、_printChanges() 输出、Console 日志或明确的交互路径。
  2. 选择诊断路径。

    • 意外的 body 调用或嘈杂的 _printChanges() 输出:阅读 references/body-reevaluation.md。
    • 状态重置、onAppear 重复触发、动画异常、焦点丢失或滚动位置重置:阅读 references/view-identity.md。
    • 列表、网格、滚动视图缓慢,或包含大量条目时初始加载缓慢:阅读 references/lazy-loading.md。
    • AnyView、在 body 中创建对象、开销较大的排序/筛选、过大的图像或重复写入状态:阅读 references/common-pitfalls.md。
  3. 在进行大范围重写之前,先进行轻量检测。

    • 在最可疑的视图中使用 let _ = Self._printChanges()。
    • 如果代码检查无法解释开销,请使用 Instruments 的 SwiftUI 模板。
    • 使用 os.Logger 或 os_signpost 为开销较大的执行路径添加自定义计数。
  4. 通过减少失效、保留标识或转移工作来修复问题。

    • 将状态下移到实际使用它的视图中。
    • 拆分大型视图,避免不相关的状态变化导致开销较大的子树重新求值。
    • 为 ForEach 和 .id() 使用稳定标识符。
    • 对于大型集合,将急切加载容器替换为 List 或惰性堆栈/网格。
    • 将繁重的格式化、解码、排序、筛选和图像准备工作移出 body。
  5. 验证问题是否解决,然后清理临时检测代码,除非用户希望保留。

快速分诊

症状首要怀疑对象参考资料
无关值变化时 body 也会运行过度观察、父视图重新创建、状态放置过高body-reevaluation.md
_printChanges() 显示 @identity changed不稳定的 .id()、条件结构变化view-identity.md
滚动视图一次性创建数百行ScrollView 中使用 VStack/HStack、未使用惰性容器lazy-loading.md
相同内容反复触发 onAppear标识 churn 或惰性单元格回收view-identity.md、lazy-loading.md
在字段中输入时整个屏幕都会更新范围过大的 ObservableObject、状态放置过高body-reevaluation.md
初始加载或动画卡顿body 中存在开销较大的工作、图像解码、急切布局common-pitfalls.md
动态视图类型的差异比较效果似乎不佳AnyView 或类型擦除的分支common-pitfalls.md

实用工具

Self._printChanges()
var body: some View {
    let _ = Self._printChanges()
    // view content
}

常见输出的含义如下:

输出含义
@self changed父视图重新创建了此视图值。检查父视图失效情况和输入是否相等。
@identity changedSwiftUI 销毁并重新创建了此视图。检查 .id()、ForEach 的 id 和分支结构。
_property changed存储属性、状态、绑定或环境值发生了变化。
Instruments

使用 Xcode 的 SwiftUI 模板检查 View Body 计数、View Properties 变化和 Core Animation 提交次数。按计数或持续时间排序,重现缓慢的交互,并将修改重点放在最耗时的视图类型上。

自定义日志

使用 os.Logger 快速统计本地次数;如果需要与 Instruments 中的时间线关联,则使用 os_signpost。

审查清单

  • 不使用不稳定的 .id() 值,例如 UUID()、Date()、随机值或可变数组索引。
  • ForEach 使用稳定且唯一的模型标识符。
  • 不要仅为了切换同一视图上的修饰符而使用条件分支。
  • 对于大型集合,使用 List、LazyVStack、LazyHStack、LazyVGrid 或 LazyHGrid。
  • 视图只观察其实际显示或修改的模型属性。
  • 在部署目标允许时,优先使用 @Observable,而非宽泛使用 ObservableObject。
  • 不要在 body 中创建格式化器、解码器、谓词、正则表达式、视图模型或解码图像。
  • 适时在发生变化时执行开销较大的筛选、排序和布局计算,将其结果缓存为状态,或在主执行器之外执行。
  • 最终交付前,移除临时的 _printChanges()、打印日志和标记点,或明确标注其用途。

API 可用性

API / 技术最低平台版本
Self._printChanges()iOS 15 / macOS 12 时代的 SwiftUI 诊断功能
ObservableObjectiOS 13 / macOS 10.15
@ObservableiOS 17 / macOS 14
LazyVStack、LazyHStack、LazyVGrid、LazyHGridiOS 14 / macOS 11
.id()iOS 13 / macOS 10.15
TimelineViewiOS 15 / macOS 12
Instruments SwiftUI 模板Xcode 14+

参考资料

  • references/body-reevaluation.md:依赖失效、_printChanges()、观察范围和状态放置。
  • references/view-identity.md:结构身份、.id()、ForEach、分支身份和状态丢失。
  • references/lazy-loading.md:急切与延迟容器、列表/网格选择、滚动性能和延迟加载的陷阱。
  • references/common-pitfalls.md:AnyView、对象分配、开销较大的 body 操作、图像解码、冗余状态和隐藏视图。
  • references/upstream-license.md:源材料的上游署名和 MIT 许可证声明。

评论

支持 Markdown