mcpField
A
axiom-energy

axiom-energy

github.com/charleswiltgen开发1.2k176 次安装今天更新
代码仓库

关于 axiom-energy

axiom-energy 是面向 iOS 开发者的能耗诊断与优化技能,强调先用 Power Profiler 测量,再针对主要耗能子系统采取措施。

主要功能

  1. 01Power Profiler 诊断流程
  2. 02CPU、GPU、网络与显示分析
  3. 03定时器和轮询优化
  4. 04延迟加载与缓存建议
  5. 05定位和后台任务指导

适用场景

  1. 01排查耗电或设备发热
  2. 02定位高能耗对应的子系统
  3. 03降低后台运行能耗
  4. 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 诊断:

  1. 打开 Library 窗格时录制跟踪记录
  2. CPU Power Impact 轨道显示出明显峰值
  3. Time Profiler 显示 VideoCardView 主体被调用数百次
  4. 根本原因: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 诊断:

  1. 用户在通勤时收集跟踪记录(设置 → 开发者 → 性能跟踪)
  2. 跟踪记录显示,CPU 周期性飙升,并与用户移动相关
  3. Time Profiler 显示 videoSuggestionsForLocation 消耗了大量 CPU
  4. 根本原因:每次位置更新时都会解析 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:音频会话始终处于活动状态的音乐应用

症状:即使没有播放音乐,应用也会消耗电池电量。

诊断:

  1. Power Profiler 显示后台 CPU 持续活动
  2. 停止播放后,音频会话仍保持活动状态
  3. 系统一直让音频硬件保持开启

修复:

// 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+ 状态:可用于生产环境的能耗优化模式

评论

支持 Markdown