Demystify SwiftUI performance
Swift & UI 进阶 20m

解密 SwiftUI 性能:视图更新的完整机制

Demystify SwiftUI performance

2023年6月5日

在 Apple 官方观看视频

一句话判断

如果你不理解 SwiftUI 的视图更新机制,就写不出高性能的代码——这场 Session 从依赖追踪到 body 重建,把 SwiftUI 刷新视图的完整生命周期拆解清楚了。

这场 Session 讲了什么

Session 建立了一个关于 SwiftUI 性能的思维模型,核心是理解视图更新的完整循环。

症状-测量-定位-优化的反馈循环。性能问题始于症状(卡顿、动画掉帧、等待光标)。解决性能问题的正确顺序是:先测量确认症状存在,再定位根因(通常是对 SwiftUI 机制的错误假设),然后优化,最后重新测量验证。

SwiftUI 的视图更新机制。SwiftUI 追踪视图的依赖(如 @State 变量)。当事件(如用户点击)到来时,如果依赖发生变化,视图被标记为”需要刷新”。在事务(Transaction)结束时,SwiftUI 调用 body 生成新的视图值。这个过程中,框架会比较新旧视图值,决定哪些部分的渲染需要更新。

性能问题的常见根源。不必要地触发 body 重建——比如把大视图的 @State 放在了不相关的父视图上。在 body 中做计算而非使用 onAppear 或缓存。视图层级过深导致比较开销大。在 List 中使用没有正确实现 Identifiable 的元素类型。

值得深挖的点

依赖追踪的粒度:SwiftUI 不是”整个 App 重建”,而是精确追踪每个视图的依赖范围。当 @State 变化时,只有依赖这个状态的视图会重建 body。理解这一点是优化 SwiftUI 性能的基础——把状态放在尽量靠近使用它的视图上。

body 重建 vs 渲染更新:body 重建(生成新的视图值)和渲染更新(修改屏幕上的像素)是两件事。body 重建后,SwiftUI 会 diff 新旧值,只更新真正变化的部分。但如果 body 重建本身很慢(比如包含大量计算),即使渲染更新很少,性能也会差。

Instruments 的 SwiftUI 模板:Session 推荐使用 Instruments 的 SwiftUI 模板来可视化视图更新的时间线,直接看到哪个视图在什么时候重建了 body。

代码片段

避免不必要 body 重建的常见模式:

// 不好的做法:父视图持有所有状态,任何变化都触发全部子视图重建
struct BadParentView: View {
    @State var selectedItem: Item?
    @State var filterText = ""
    @State var sortOrder: SortOrder = .name

    var body: some View {
        // 这三个状态任何一个变化,整个 body 都会重建
        VStack {
            FilterBar(text: $filterText, order: $sortOrder)
            ItemList(filter: filterText, order: sortOrder,
                     selectedItem: $selectedItem)
            DetailView(item: selectedItem)  // 不依赖 filter/sort
        }
    }
}

// 好的做法:把状态推到真正需要的子视图上
struct GoodParentView: View {
    // 只保留全局需要的状态
    var body: some View {
        VStack {
            FilterSection()  // 内部管理自己的 filter/sort 状态
            ItemSection()    // 内部管理 selectedItem
        }
    }
}

最佳实践

  • 把状态尽量放在靠近使用它的子视图上,减少 body 重建的范围
  • body 中避免做耗时的计算——用 onAppear 预加载或缓存结果
  • List 的元素正确实现 Identifiable,帮助 SwiftUI 高效 diff
  • 使用 Instruments 的 SwiftUI 模板可视化视图更新时间线
  • 遵循”测量 -> 定位 -> 优化 -> 验证”的循环,不要凭直觉优化

还有什么值得关注

  • SwiftUI 的视图 diff 算法的具体实现
  • @Observable(iOS 17)vs @StateObject vs @ObservedObject 的性能差异
  • EquatableViewview.body.isEqual 的使用场景
  • SwiftUI 与 Instruments 集成的详细用法
WWDC 2023