Profile and optimize power usage in your app
System Services 高级 1m

Profile and optimize power usage in your app

2025年6月9日

在 Apple 官方观看视频

一句话判断

Power Profiler 终于可以在设备上独立采集了 — 不用连 Xcode,让 QA 在真实场景下跑几小时 trace,回来在 Instruments 里分析 CPU/GPU/网络的功耗影响,这才是发现”办公室里复现不了的耗电问题”的正确姿势。

这场 Session 讲了什么

Wiam 从四个场景展开 power optimization 的完整方法论:

可复现问题的调试:用 Power Profiler(Instruments 模板)在设备上录制功耗 trace。案例中 Destination Video app 的 Library pane 打开时 CPU power impact 从 1 飙升到 21,通过 Time Profiler 定位到 VideoCardView 被大量创建。根因是 VStack 一次性加载所有视频缩略图。修复方案:替换为 LazyVStack,CPU impact 降至 4.3。

不可复现问题的发现:新增设备端功耗采集模式。在 Settings -> Developer -> Performance Trace 中开启 Power Profiler,指定目标 app,通过 Control Center 的 Performance Trace 图标开始/停止采集。支持运行数小时。同事在通勤场景下采集的 trace 揭示了 videoSuggestionsForLocation 每次位置变化都重新解析大型 JSON 文件的问题 — 修复方案是懒加载+缓存。

方案对比:在两种实现方案之间做功耗对比时,多次运行取平均值,考虑热状态、设备状态、系统压力等变量。

主动优化策略:Xcode Energy Gauges(实时反馈)-> Instruments Power Profiler(深度分析)-> XCTests(自动化检测)-> Xcode Organizer / MetricKit(上线后监控)-> App Store Connect API(远端数据)。

值得深挖的点

  1. 设备端功耗采集是 game changer。以前只有连着 Xcode 的受控环境才能做 power profiling,但很多耗电问题只有在真实使用场景(导航、通勤、户外)中才会触发。现在你可以让测试人员带着手机正常使用几小时,回传 trace 文件分析。

  2. LazyVStack vs VStack 的功耗差异可以达到 5 倍(CPU impact 21 vs 4.3)。这不是性能优化的小技巧,而是直接影响电池续航的架构决策。任何大量数据的列表/网格都应该用 lazy 容器。

  3. JSON 解析的重复调用是常见的隐蔽耗电源videoSuggestionsForLocation 每次位置变化都解析数百条规则的 JSON 文件,在通勤场景下位置频繁变化,导致周期性 CPU 尖峰。懒加载+缓存是最直接的修复。

  4. Power Profiler 的四个子指标:CPU、GPU、Display、Networking power impact。不需要每个都深挖,先看哪个指标有异常尖峰,再用对应的 profiler(Time Profiler、GPU Profiler 等)定位根因。

代码片段

VStack -> LazyVStack 修复

// 修复前:一次性创建所有视图
VStack {
    ForEach(videos) { video in
        VideoCardView(video: video) // 每个都立即创建缩略图
    }
}

// 修复后:按需创建
LazyVStack {
    ForEach(videos) { video in
        VideoCardView(video: video) // 只创建可见的
    }
}

JSON 缓存优化

// 修复前:每次都解析
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    let data = try! Data(contentsOf: rulesURL)
    let rules = try! JSONDecoder().decode([Rule].self, from: data)
    return filterVideos(by: rules, location: location)
}

// 修复后:懒加载缓存
private var cachedRules: [Rule]?
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    if cachedRules == nil {
        let data = try! Data(contentsOf: rulesURL)
        cachedRules = try! JSONDecoder().decode([Rule].self, from: data)
    }
    return filterVideos(by: cachedRules!, location: location)
}

最佳实践

  1. 现在就跑一次 Power Profiler trace。连上设备,Profile 你的 app,看看有没有意外的 CPU/GPU 尖峰。这是成本最低的功耗审计。

  2. 启用设备端 Performance Trace 做真实场景测试。让 QA 或 beta 用户在不同使用场景下采集 trace,特别是导航、长时间后台、弱网等环境。

  3. 所有列表/网格默认使用 Lazy 容器。除非你确定数据量很小(< 20 项),否则没有理由用 VStack/HStack 代替 LazyVStack/LazyHStack。

  4. 避免在频繁调用的回调中做文件 I/O 或 JSON 解析。位置更新、timer 回调、通知处理等高频触发点应该只做增量计算,重活缓存或预计算。

  5. 建立功耗基线。用 Power Profiler 记录当前版本的功耗数据,每次修改后对比,防止”优化一个功能,引入另一个耗电问题”。

还有什么值得关注

  • iOS 26 的电池设置新增了详细的 app 级功耗分解,用户可以看到每个 app 的耗电占比 — 这会提高用户对功耗问题的敏感度。
  • Low Power Mode 在 macOS Tahoe 中针对游戏做了优化,开发者可以在 Low Power Mode 下启用更省电的游戏设置。
  • MetricKit 可以在 app 上线后持续收集功耗数据,配合 App Store Connect API 做长期趋势分析。
系统服务 开发工具