关于 axiom-energy
axiom-energy 是面向 iOS 开发者的能耗诊断与优化技能,强调先用 Power Profiler 测量,再针对主要耗能子系统采取措施。
主要功能
- 01Power Profiler 诊断流程
- 02CPU、GPU、网络与显示分析
- 03定时器和轮询优化
- 04延迟加载与缓存建议
- 05定位和后台任务指导
适用场景
- 01排查耗电或设备发热
- 02定位高能耗对应的子系统
- 03降低后台运行能耗
- 04对比优化前后的功耗轨迹
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-energy安装后在 Claude Code 中输入/axiom-energy确认已加载。
详细介绍
能耗优化
概述
能耗问题表现为电池耗电、设备发热和 App Store 评价不佳。核心原则:优化前先测量。使用 Power Profiler 找出主要子系统(CPU/GPU/Network/Location/Display),然后有针对性地修复。
关键见解:开发者往往不知道从哪里开始审查。本技能提供系统化诊断,而非猜测。
要求:iOS 26+、Xcode 26+、Instruments 中的 Power Profiler
示例提示词
开发者提出的实际问题,本技能可以解答:
1. “我的应用总是出现在‘电池’设置的耗电排行顶部。如何找出耗电原因?”
→ 本技能介绍 Power Profiler 工作流,帮助找出主要子系统并进行有针对性的修复
2. “用户反映我的应用会让手机发烫。我该从哪里开始调试?”
→ 本技能提供决策树:通过特定模式诊断 CPU、GPU 或 Network
3. “我用了计时器和位置更新。它们会导致电池耗电吗?”
→ 本技能介绍计时器容差、位置精度的权衡以及审查清单
4. “即使用户没有使用应用,它在后台也会耗电。”
→ 本技能介绍后台执行模式、BGTasks 和 EMRCA 原则
5. “如何测量优化是否真的延长了电池续航?”
→ 本技能演示使用 Power Profiler 进行优化前后对比的工作流
警示信号——很可能存在高能耗
如果出现以下任意情况,请怀疑存在能效问题:
- “电池”设置:你的应用持续位居耗电应用排行顶部
- 设备温度:正常使用应用时手机会发热
- 用户评价:提到“电池耗电”“手机发烫”“耗光我的电池”
- Xcode 能耗计:持续显示高或非常高的能耗影响
- 后台运行时长:应用在不可见时运行时间超出预期
- 网络活动:频繁发送小请求,而不是批量操作
- 位置图标:应用不应使用位置时,状态栏却显示该图标
与正常能耗的区别
- 正常:应用在活跃使用期间耗能,进入后台后耗能极少
- 问题:即使用户没有交互,应用仍会消耗大量能量
必须先执行的步骤
优化代码前,务必先运行 Power Profiler:
步骤 1:记录能耗轨迹(5 分钟)
1. Connect iPhone wirelessly to Xcode (wireless debugging)
2. Xcode → Product → Profile (Cmd+I)
3. Select Blank template
4. Click "+" → Add "Power Profiler" instrument
5. Optional: Add "CPU Profiler" for correlation
6. Click Record
7. Use your app normally for 2-3 minutes
8. Click Stop
使用无线连接的原因:设备通过线缆充电时,能耗指标会显示为 0。使用无线调试以获得准确读数。
步骤 2:找出主要子系统
展开 Power Profiler 轨道,检查各应用的指标:
| 轨道 | 含义 | 高值表示 |
|---|---|---|
| CPU Power Impact | 处理器活动 | 计算、计时器、解析 |
| GPU Power Impact | 图形渲染 | 动画、模糊、Metal |
| Display Power Impact | 屏幕使用情况 | 亮度、常显内容 |
| Network Power Impact | 无线电活动 | 请求、下载、轮询 |
请留意:应用使用期间,哪个子系统的持续数值最高。
步骤 3:转到针对相应子系统的修复方法
找出主要子系统后,使用下方的决策树。
诊断结果说明
- CPU 为主 → 检查计时器、轮询、JSON 解析和急切加载
- GPU 为主 → 检查动画、模糊效果和帧率
- Network 为主 → 检查请求频率,以及轮询与推送的选择
- Display 为主 → 检查深色模式、亮度和屏幕开启时长
- Location(显示在 CPU 中)→ 检查精度和更新频率
为什么要先诊断
- 使用 Power Profiler 找出根本原因:15–20 分钟
- 猜测并测试各种随机优化:4 小时以上,而且经常找错子系统
能耗决策树
User reports energy issue?
│
├─ CPU Power Impact dominant?
│ ├─ Continuous high impact?
│ │ ├─ Timers running? → Pattern 1: Timer Efficiency
│ │ ├─ Polling data? → Pattern 2: Push vs Poll
│ │ └─ Processing in loop? → Pattern 3: Lazy Loading
│ ├─ Spikes during specific actions?
│ │ ├─ JSON parsing? → Cache parsed results
│ │ ├─ Image processing? → Move to background, cache
│ │ └─ Database queries? → Index, batch, prefetch
│ └─ High background CPU?
│ ├─ Location updates? → Pattern 4: Location Efficiency
│ ├─ BGTasks running too long? → Pattern 5: Background Execution
│ └─ Audio session active? → Stop when not playing
│
├─ Network Power Impact dominant?
│ ├─ Many small requests?
│ │ └─ Batch into fewer large requests
│ ├─ Polling pattern detected?
│ │ └─ Convert to push notifications → Pattern 2
│ ├─ Downloads in foreground?
│ │ └─ Use discretionary background URLSession
│ └─ High cellular usage?
│ └─ Defer to WiFi when possible
│
├─ GPU Power Impact dominant?
│ ├─ Continuous animations?
│ │ └─ Stop when view not visible
│ ├─ Blur effects (UIVisualEffectView)?
│ │ └─ Reduce or remove, use solid colors
│ ├─ High frame rate animations?
│ │ └─ Audit secondary frame rates → Pattern 6
│ └─ Metal rendering?
│ └─ Implement frame limiting
│
├─ Display Power Impact dominant?
│ ├─ Light backgrounds on OLED?
│ │ └─ Implement Dark Mode (up to 70% savings)
│ ├─ High brightness content?
│ │ └─ Use darker UI elements
│ └─ Screen always on?
│ └─ Allow screen to sleep when appropriate
│
└─ Location causing drain? (check CPU lane + location icon)
├─ Continuous updates?
│ └─ Switch to significant-change monitoring
├─ High accuracy (kCLLocationAccuracyBest)?
│ └─ Reduce to kCLLocationAccuracyHundredMeters
└─ Background location?
└─ Evaluate if truly needed → Pattern 4
常见能耗模式(及修复方法)
模式 1:计时器效率
问题:计时器会唤醒处于空闲状态的 CPU,消耗大量能量。
❌ 反面模式——未设置容差的计时器
// BAD: Timer fires exactly every 1.0 seconds
// Prevents system from batching with other timers
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.updateUI()
}
✅ 修复方法——为计时器批处理设置容差
// GOOD: 10% tolerance allows system to batch timers
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.updateUI()
}
timer.tolerance = 0.1 // 10% tolerance minimum
// BETTER: Use Combine Timer with tolerance
Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .default)
.autoconnect()
.sink { [weak self] _ in
self?.updateUI()
}
.store(in: &cancellables)
✅ 最佳实践 — 使用事件驱动,而非轮询
// BEST: Don't use timer at all — react to events
NotificationCenter.default.publisher(for: .dataDidUpdate)
.sink { [weak self] _ in
self?.updateUI()
}
.store(in: &cancellables)
要点:
- 将容差设置为间隔的至少 10%
- 计时器容差允许系统将多个计时器合并为一次唤醒
- 优先采用事件驱动模式,而不是轮询计时器
- 不再需要计时器时,务必使其失效
模式 2:推送与轮询
问题:轮询(每隔 N 秒检查服务器)会让无线电保持活跃状态,消耗电池电量。
❌ 反模式 — 每 5 秒轮询一次
// BAD: Polls server every 5 seconds
// Radio stays active, massive battery drain
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
self?.fetchLatestData() // Network request every 5 seconds
}
✅ 修复方案 — 使用后台推送通知
// GOOD: Server pushes when data changes
// Radio only active when there's actual new data
// 1. Register for remote notifications
UIApplication.shared.registerForRemoteNotifications()
// 2. Handle background notification
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let _ = userInfo["content-available"] else {
completionHandler(.noData)
return
}
Task {
do {
let hasNewData = try await fetchLatestData()
completionHandler(hasNewData ? .newData : .noData)
} catch {
completionHandler(.failed)
}
}
}
后台推送的服务器负载:
{
"aps": {
"content-available": 1
},
"custom-data": "your-payload"
}
要点:
- 后台推送是非强制的——系统会在最佳时机送达
- 对于非紧急更新,使用
apns-priority: 5(能效更高) - 仅对有时效性的提醒使用
apns-priority: 10 - 每 5 秒轮询一次的能耗是推送的100 倍
模式 3:延迟加载与缓存
问题:一开始就加载所有数据会导致 CPU 突增和内存压力。
❌ 反模式 — 急切加载(摘自 WWDC25-226)
// BAD: Creates and renders ALL views upfront
// From WWDC25-226: This caused CPU spike and hang
VStack {
ForEach(videos) { video in
VideoCardView(video: video) // Creates ALL thumbnails immediately
}
}
✅ 修复方案 — 延迟加载
// GOOD: Only creates visible views
// From WWDC25-226: Reduced CPU power impact from 21 to 4.3
LazyVStack {
ForEach(videos) { video in
VideoCardView(video: video) // Creates on-demand
}
}
❌ 反模式 — 重复解析(摘自 WWDC25-226)
// BAD: Parses JSON file on every location update
// From WWDC25-226: Caused continuous CPU drain during commute
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
// Called every location change!
let data = try? Data(contentsOf: rulesFileURL)
let rules = try? JSONDecoder().decode([RecommendationRule].self, from: data)
return filteredVideos(using: rules)
}
✅ 修复方案 — 缓存解析后的数据
// GOOD: Parse once, reuse cached result
// From WWDC25-226: Eliminated CPU drain
private lazy var cachedRules: [RecommendationRule] = {
let data = try? Data(contentsOf: rulesFileURL)
return (try? JSONDecoder().decode([RecommendationRule].self, from: data)) ?? []
}()
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
return filteredVideos(using: cachedRules) // No parsing!
}
要点:
- 对大型集合使用
LazyVStack、LazyHStack、LazyVGrid - 缓存解析后的 JSON、解码后的数据和计算结果
- 将开销大的操作移出频繁调用的方法
模式 4:高效定位
问题:持续更新位置会让 GPS 保持活跃状态,快速消耗电池电量。
❌ 反模式 — 持续进行高精度更新
// BAD: Continuous updates with best accuracy
// GPS stays active constantly, massive battery drain
let locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation() // Never stops!
✅ 修复方案 — 适当的精度与显著位置变化检测
// GOOD: Reduced accuracy, significant-change monitoring
let locationManager = CLLocationManager()
// Use appropriate accuracy (100m is fine for most apps)
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
// Use distance filter to reduce updates
locationManager.distanceFilter = 100 // Only update every 100 meters
// For background: Use significant-change monitoring
locationManager.startMonitoringSignificantLocationChanges()
// Stop when done
func stopTracking() {
locationManager.stopUpdatingLocation()
locationManager.stopMonitoringSignificantLocationChanges()
}
✅ 更佳方案 — iOS 26+ 使用支持静止检测的 CLLocationUpdate
// BEST: Modern async API with automatic stationary detection
for try await update in CLLocationUpdate.liveUpdates() {
if update.stationary {
// Device stopped moving — system pauses updates automatically
// Switch to CLMonitor for region monitoring
break
}
handleLocation(update.location)
}
精度对比(对电池的影响):
| 精度 | 电池影响 | 使用场景 |
|---|---|---|
kCLLocationAccuracyBest | 非常高 | 仅用于导航应用 |
kCLLocationAccuracyNearestTenMeters | 高 | 健身追踪 |
kCLLocationAccuracyHundredMeters | 中 | 门店定位 |
kCLLocationAccuracyKilometer | 低 | 天气应用 |
| 显著位置变化 | 非常低 | 后台更新 |
模式 5:后台执行(EMRCA)
问题:运行时间过长或执行过于频繁的后台任务会消耗电池电量。
EMRCA 原则(摘自 WWDC25-227)
后台工作必须做到:
- E 高效(Efficient)——设计轻量、目标明确的任务
- M 最少(Minimal)——尽量减少后台工作
- R 稳健(Resilient)——保存增量进度;响应过期信号
- C 周全(Courteous)——尊重用户偏好和系统条件
- A 自适应(Adaptive)——了解并适应系统优先级
❌ 反模式 — 长时间运行的后台任务
// BAD: Requests unlimited background time
// System will terminate after ~30 seconds anyway
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTask = application.beginBackgroundTask {
// Expiration handler — but task runs too long
}
// Long operation that may not complete
performLongOperation()
}
✅ 修复方案 — 正确处理后台任务
// GOOD: Finish quickly, save progress, notify system
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTask = application.beginBackgroundTask(withName: "Save State") { [weak self] in
// Expiration handler — clean up immediately
self?.saveProgress()
if let task = self?.backgroundTask {
application.endBackgroundTask(task)
}
self?.backgroundTask = .invalid
}
// Quick operation
saveEssentialState()
// End task as soon as done — don't wait for expiration
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
✅ 长时间操作 — 使用 BGProcessingTask
// BEST: Let system schedule at optimal time (charging, WiFi)
func scheduleBackgroundProcessing() {
let request = BGProcessingTaskRequest(identifier: "com.app.maintenance")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = true // Only when charging
try? BGTaskScheduler.shared.submit(request)
}
// Register handler at app launch
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.maintenance",
using: nil
) { task in
self.handleMaintenance(task: task as! BGProcessingTask)
}
✅ iOS 26+ — 用户发起的工作使用 BGContinuedProcessingTask
// NEW iOS 26: Continue user-initiated tasks with progress UI
let request = BGContinuedProcessingTaskRequest(
identifier: "com.app.export",
title: "Exporting Photos",
subtitle: "23 of 100 photos"
)
try? BGTaskScheduler.shared.submit(request)
模式 6:帧率审查
问题:次要动画以高于所需的帧率运行,会增加 GPU 功耗。
❌ 反模式 — 不受控的帧率
// BAD: Secondary animation runs at 60fps
// When primary content only needs 30fps, this wastes power
UIView.animate(withDuration: 2.0, delay: 0, options: [.repeat]) {
self.subtitleLabel.alpha = 0.5
} completion: { _ in
self.subtitleLabel.alpha = 1.0
}
✅ 修复方案 — 使用 CADisplayLink 控制帧率
// GOOD: Explicitly set preferred frame rate
let displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
displayLink.preferredFrameRateRange = CAFrameRateRange(
minimum: 10,
maximum: 30, // Match primary content
preferred: 30
)
displayLink.add(to: .current, forMode: .default)
摘自 WWDC22-10083:让次要动画的帧率与主要内容保持一致,最多可节省 20% 的电池电量。
审查清单
计时器审查
- 是否为所有计时器设置了容差(≥间隔的 10%)?
- 不再需要计时器时是否已将其失效?
- 在可行的情况下,是否使用 Combine Timer 而非 NSTimer?
- 是否存在可以改用推送通知的轮询模式?
- 应用进入后台时是否停止了计时器?
网络审查
- 是否将请求批量处理,而不是发送许多小请求?
- 对于非紧急下载,是否使用了非强制 URLSession?
- 是否设置了
waitsForConnectivity,以避免连接尝试失败? - 对于可延后处理的工作,是否将
allowsExpensiveNetworkAccess设置为 false? - 是否使用推送通知而非轮询?
位置审查
- 是否使用了恰当的精度(除导航场景外,是否避免使用
kCLLocationAccuracyBest)? - 是否设置了
distanceFilter来降低更新频率? - 不再需要时是否停止更新?
- 后台更新是否使用了重大位置变化?
- 是否有充分理由在后台获取位置,并向用户说明?
后台执行审查
- 工作完成后是否及时调用
endBackgroundTask? - 长时间运行的操作是否使用了设置
requiresExternalPower的BGProcessingTask? - Info.plist 中的后台模式是否仅限于实际需要的模式?
- 未播放时是否停用了音频会话?
- 是否遵循了 EMRCA 原则?
显示/GPU 审查
- 是否支持深色模式(OLED 可节省 70% 电量)?
- 视图不可见时是否停止动画?
- 辅助动画是否使用了恰当的帧率?
- 是否尽量减少或移除了模糊效果?
- Metal 渲染是否限制了帧率?
磁盘 I/O 审查
- 是否将写入操作批量处理,而不是频繁进行小规模写入?
- SQLite 是否使用 WAL 日志模式?
- 是否避免了快速创建和删除文件?
- 对于频繁更新的数据,是否使用 SwiftData/Core Data,而不是序列化文件?
压力场景
场景 1:“每 5 秒轮询一次,以实现实时更新”
诱惑:“推送通知很复杂。轮询更简单。”
现实:
- 每 5 秒轮询一次:无线电 100% 时间处于活动状态
- 推送通知:仅在数据变化时激活无线电
- 用户一定会在电池设置中看到你的应用排在前列
- App Store 评论一定会提到“耗电严重”
时间成本比较:
- 实现轮询:30 分钟
- 实现推送:2-4 小时
- 修复差评和声誉损害:数周
反对建议模板:“推送通知只需几小时即可完成设置,但轮询会让我们的应用稳居电池设置的耗电榜首。用户会主动卸载耗电的应用。投入 2 小时,可以避免持续的声誉损害。”
场景 2:“持续获取位置,以获得最高精度”
诱惑:“用户期待精准定位。我们就用 kCLLocationAccuracyBest 吧。”
现实:
kCLLocationAccuracyBest:GPS + WiFi + 蜂窝网络三角定位 = 耗电量巨大kCLLocationAccuracyHundredMeters:足以满足 95% 的使用场景- 状态栏中的位置图标会让用户去查看电池设置
时间成本比较:
- 实现高精度定位:10 分钟
- 调查“为什么我的应用这么耗电”的投诉:数小时
- 重构为恰当的精度:30 分钟
反对建议模板:“对于[使用场景],100 米精度已经足够。Google Maps 这样的导航应用需要最高精度,但我们只是显示[商店位置/天气/大致区域]。用户几乎察觉不到精度差异,但耗电量差别巨大。”
场景 3:“保持动画运行,用户期待流畅的界面”
诱惑:“动画能让应用显得更生动、更精致。”
现实:
- 视图不可见时仍运行动画 = 纯粹浪费
- 辅助动画使用高帧率 = 消耗 GPU 电量
- GPU 功耗占设备总功耗的比例相当高
时间成本比较:
- 添加动画:15 分钟
- 添加可见性检查:额外 5 分钟
- 调查“手机发烫”的报告:数小时
反对建议模板:“我们可以保留动画,但应在视图不可见时暂停。这只需 5 分钟的改动,就能避免用户没有看屏幕时仍消耗 GPU 电量。”
场景 4:“先发布,之后再优化”
诱惑:“能耗优化只是锦上添花。我们可以等到 v1.1 再做。”
现实:
- 电池耗电会立即被用户察觉
- 第一印象会影响评价
- “耗电大户”的名声很难摆脱
- Power Profiler 基准测试只需 15 分钟
时间成本对比:
- 发布前检查 Power Profiler:15 分钟
- 发布后修复能耗问题:数天(还会损害声誉)
- 重获用户信任:数月
回应模板:“发布前用 15 分钟运行 Power Profiler,就能发现严重的能耗问题。如果带着电池问题发布,用户第一天就会在电池设置中看到我们的应用排在最前面,还会留下 1 星评价。让我快速检查一下——这比事后补救更快。”
实际案例
示例 1:采用急切加载的视频流应用(WWDC25-226)
症状:打开 Library 窗格时,CPU 功耗影响值从 1 飙升到 21。界面卡住。
使用 Power Profiler 诊断:
- 打开 Library 窗格时录制跟踪记录
- CPU Power Impact 轨道显示出明显峰值
- Time Profiler 显示
VideoCardView主体被调用数百次 - 根本原因:
VStack一次性创建了所有视频缩略图
修复:
// Before: VStack (eager)
VStack {
ForEach(videos) { video in
VideoCardView(video: video)
}
}
// After: LazyVStack (on-demand)
LazyVStack {
ForEach(videos) { video in
VideoCardView(video: video)
}
}
结果:CPU 功耗影响值从 21 降至 4.3。界面不再卡住。
示例 2:反复解析数据的位置建议功能(WWDC25-226)
症状:通勤用户报告电池耗电严重。开发者在办公桌前无法复现。
使用设备端 Power Profiler 诊断:
- 用户在通勤时收集跟踪记录(设置 → 开发者 → 性能跟踪)
- 跟踪记录显示,CPU 周期性飙升,并与用户移动相关
- Time Profiler 显示
videoSuggestionsForLocation消耗了大量 CPU - 根本原因:每次位置更新时都会解析 JSON 文件
修复:
// Before: Parse on every call
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
let data = try? Data(contentsOf: rulesFileURL)
let rules = try? JSONDecoder().decode([RecommendationRule].self, from: data)
return filteredVideos(using: rules)
}
// After: Parse once, cache
private lazy var cachedRules: [RecommendationRule] = {
let data = try? Data(contentsOf: rulesFileURL)
return (try? JSONDecoder().decode([RecommendationRule].self, from: data)) ?? []
}()
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
return filteredVideos(using: cachedRules)
}
结果:消除了移动期间的 CPU 峰值。解决了电池耗电问题。
示例 3:音频会话始终处于活动状态的音乐应用
症状:即使没有播放音乐,应用也会消耗电池电量。
诊断:
- Power Profiler 显示后台 CPU 持续活动
- 停止播放后,音频会话仍保持活动状态
- 系统一直让音频硬件保持开启
修复:
// Before: Never deactivate
func playTrack(_ track: Track) {
try? AVAudioSession.sharedInstance().setActive(true)
player.play()
}
func stopPlayback() {
player.stop()
// Audio session still active!
}
// After: Deactivate when done
func stopPlayback() {
player.stop()
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
}
// Even better: Use AVAudioEngine auto-shutdown
let engine = AVAudioEngine()
engine.isAutoShutdownEnabled = true // Automatically powers down when idle
结果:后台音频硬件关闭。电池耗电问题得到解决。
响应低电量模式
检测用户是否启用了低电量模式,并相应地调整:
// Check current state
if ProcessInfo.processInfo.isLowPowerModeEnabled {
reduceEnergyUsage()
}
// React to changes
NotificationCenter.default.publisher(for: .NSProcessInfoPowerStateDidChange)
.sink { [weak self] _ in
if ProcessInfo.processInfo.isLowPowerModeEnabled {
self?.reduceEnergyUsage()
} else {
self?.restoreNormalOperation()
}
}
.store(in: &cancellables)
func reduceEnergyUsage() {
// Pause optional activities
// Reduce animation frame rates
// Increase timer intervals
// Defer network requests
// Stop location updates if not critical
}
在生产环境中监控能耗
MetricKit 设置
import MetricKit
class EnergyMetricsManager: NSObject, MXMetricManagerSubscriber {
static let shared = EnergyMetricsManager()
func startMonitoring() {
MXMetricManager.shared.add(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
if let cpuMetrics = payload.cpuMetrics {
// Monitor CPU time
let foregroundCPU = cpuMetrics.cumulativeCPUTime
logMetric("foreground_cpu", value: foregroundCPU)
}
if let locationMetrics = payload.locationActivityMetrics {
// Monitor location usage
let backgroundLocation = locationMetrics.cumulativeBackgroundLocationTime
logMetric("background_location", value: backgroundLocation)
}
}
}
}
Xcode Organizer
在 Xcode Organizer 的 电池使用情况 窗格中查看实际使用数据:
- 前台与后台能耗细分
- 类别细分(音频、网络、处理、显示等)
- 对比不同版本以检测性能退化
快速参考
Power Profiler 工作流
1. Connect device wirelessly
2. Product → Profile → Blank → Add Power Profiler
3. Record 2-3 minutes of usage
4. Identify dominant subsystem (CPU/GPU/Network/Display)
5. Apply targeted fix from patterns above
6. Record again to verify improvement
主要节能措施
| 优化 | 预期节省 |
|---|---|
| 在 OLED 屏幕上使用深色模式 | 显示功耗最多降低 70% |
| 帧率对齐 | GPU 功耗最多降低 20% |
| 推送替代轮询 | 网络效率提高 100 倍 |
| 降低位置精度 | GPS 功耗降低 50-90% |
| 计时器容差 | 显著减少 CPU 消耗 |
| 延迟加载 | 消除启动时的 CPU 峰值 |
相关技能
axiom-energy-ref— 完整 API 参考,包含所有代码示例axiom-energy-diag— 基于症状的故障排查决策树axiom-background-processing— 后台任务机制(解释任务为何不会运行)axiom-performance-profiling— 通用 Instruments 工作流axiom-memory-debugging— 内存泄漏诊断(通常与能耗有关)axiom-networking— 网络优化模式axiom-timer-patterns— 计时器崩溃预防与生命周期安全
WWDC Sessions
- WWDC25-226 “在应用中分析并优化耗电量” — Power Profiler 工作流
- WWDC25-227 “在后台完成任务” — BGContinuedProcessingTask、EMRCA
- WWDC22-10083 “降低功耗:改善电池续航” — 深色模式、帧率、延迟处理
- WWDC20-10095 “推送通知入门” — 推送与轮询
- WWDC19-417 “改善电池续航和性能” — MetricKit
最后更新:2025-12-26 平台:iOS 26+、iPadOS 26+ 状态:可用于生产环境的能耗优化模式