## 版本与工具校验


xcodebuild -version
xcrun xctrace list templates

输出应包含 Xcode 版本与可用的 Instruments 模板(如 Time Profiler、SwiftUI)。确保在真机或模拟器上进行渲染分析。


## 用 Equatable 降低无效重绘


import SwiftUI

struct RowModel: Equatable, Identifiable {
    let id: UUID
    let title: String
    let value: Int
}

struct RowView: View, Equatable {
    let model: RowModel
    static func == (lhs: RowView, rhs: RowView) -> Bool {
        lhs.model == rhs.model
    }
    var body: some View {
        HStack {
            Text(model.title)
            Spacer()
            Text("\(model.value)")
        }
    }
}

struct ContentView: View {
    @State private var items: [RowModel] = (0..<1000).map { i in
        RowModel(id: UUID(), title: "Item \(i)", value: i)
    }
    var body: some View {
        List(items.indices, id: \._self) { i in
            RowView(model: items[i]).equatable() // 可验证:仅变化的行会重绘
        }
        .onAppear {
            // 模拟增量更新
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                items[500].value += 1
            }
        }
    }
}

验证点:

  • `.equatable()` 包装 `RowView`,基于 `==` 比较避免未变化行的重绘。
  • 在 Instruments 的“SwiftUI”或“Time Profiler”中观察更新时仅 1 行触发绘制。

## @MainActor:保证 UI 状态在主隔离更新


import SwiftUI

@MainActor
final class Store: ObservableObject {
    @Published var text: String = ""
}

struct VM {
    func load() async throws -> String {
        try await Task.sleep(nanoseconds: 200_000_000)
        return "loaded"
    }
}

struct Page: View {
    @StateObject private var store = Store()
    private let vm = VM()
    var body: some View {
        VStack {
            Text(store.text)
            Button("Fetch") {
                Task {
                    let s = try await vm.load()
                    await MainActor.run { store.text = s } // 可验证:UI 更新在主隔离
                }
            }
        }
    }
}

验证点:

  • 通过 `MainActor.run` 在异步任务完成后切回主隔离更新 `@Published`,避免跨隔离写入导致的竞态或警告。

## 减少 body 计算与布局开销


  • 将昂贵计算下沉到 `ViewModel`,在 `View` 中只绑定已处理好的数据。
  • 对稳定子树使用 `@ViewBuilder` 拆分与 `Equatable`,减少 diff 与布局次数。
  • 避免在 `body` 内创建临时 `DateFormatter`/`NumberFormatter`;使用静态缓存或注入。

## Instruments 实证流程


1. 使用 `xcrun xctrace list templates` 确认模板可用。

2. 打开 Xcode → Product → Profile → 选择 Time Profiler 或 SwiftUI。

3. 交互触发更新(如上示例的 `items[500]` 变化),观察仅目标行的绘制与布局。

4. 记录 `CPU 时间`、`层次结构视图` 的更新范围与次数,作为回归基线。


## 注意事项


  • 过度使用 `.equatable()` 可能隐藏状态变化;仅在明确稳定子树时使用。
  • 对大列表使用 `id` 稳定化与差分更新(如 `DiffableDataSource` 或自定义分区),减少大规模重排。
  • 在 `Task` 内避免阻塞式 IO;使用 `await` 与超时/取消确保可预期延迟。

## 结语


SwiftUI 的性能优化关键在“减少不必要的重绘”和“保证状态在主隔离更新”。配合 Instruments 做可验证的基线与回归,可以在复杂界面中保持流畅与稳定。



点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部