跳到主要内容

SwiftUI 性能优化

问题

SwiftUI 的性能瓶颈在哪里?如何减少不必要的视图重建?

答案

View 重建原理

@State@Binding 等状态变化时,SwiftUI 会重新调用相关 View 的 body。但调用 body ≠ 重新渲染——SwiftUI 内部会 Diff 新旧 View 树,只更新变化的部分。

性能问题出现在:body 计算过于复杂或被不必要地频繁调用。

常见优化手段

1. 拆分小视图

// ❌ 巨型 body
struct ContentView: View {
@State var count = 0
@State var name = ""

var body: some View {
VStack {
Text("Count: \(count)") // count 变化时,name 相关视图也重建
TextField("Name", text: $name)
// ... 100 行 UI 代码
}
}
}

// ✅ 拆分后,各子视图独立刷新
struct CounterSection: View {
@Binding var count: Int
var body: some View {
Text("Count: \(count)")
}
}

2. 使用 Equatable 避免无效更新

struct ExpensiveView: View, Equatable {
let data: SomeData

static func == (lhs: Self, rhs: Self) -> Bool {
lhs.data.id == rhs.data.id // 只有 id 变化才更新
}

var body: some View {
// 复杂的渲染...
}
}

// 使用
ExpensiveView(data: item)
.equatable() // 启用 Equatable 检查

3. @Observable 精准追踪(iOS 17+)

@Observable
class ViewModel {
var title = "" // 只有用到 title 的视图才刷新
var subtitle = "" // 只有用到 subtitle 的视图才刷新
var unused = 0 // 没有被 body 读取,变化不会触发任何刷新
}

4. 避免在 body 中做复杂计算

// ❌
var body: some View {
let filtered = items.filter { $0.isActive }.sorted() // 每次重建都计算
List(filtered) { ... }
}

// ✅ 使用缓存
@State private var filtered: [Item] = []

var body: some View {
List(filtered) { ... }
.onChange(of: items) { _, newValue in
filtered = newValue.filter { $0.isActive }.sorted()
}
}

5. 列表优化

// ✅ 使用 LazyVStack/LazyVGrid
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemRow(item: item)
}
}
}

// ✅ id 参数确保正确 Diff
ForEach(items, id: \.uniqueID) { item in ... }

调试工具

// 打印 View body 被调用的时机
let _ = Self._printChanges()

// Instruments → SwiftUI → View Body
// 可以看到哪些 View 被频繁重建

常见面试问题

Q1: SwiftUI 列表性能和 UITableView 比怎么样?

答案

  • List + LazyVStack:iOS 15+ 性能接近 UITableView(底层用 UICollectionView)
  • 复杂 Cell + 大数据量:UIKit 仍有优势(可以用 UIViewRepresentable 桥接)
  • iOS 17+ 性能大幅改善,多数场景不需要回落到 UIKit

Q2: 如何定位 SwiftUI 性能问题?

答案

  1. Self._printChanges():打印 body 重建原因
  2. Instruments → SwiftUI:分析 View 创建和 body 调用
  3. Instruments → Time Profiler:找到 CPU 热点
  4. 检查 @StateObject vs @ObservedObject 使用是否正确(避免意外重建)

相关链接