关于 axiom-energy-ref
这是一份降低 iOS 应用能耗的 API 参考,结合 Apple 文档和 WWDC 示例,涵盖能耗分析以及计时器、网络、定位、后台任务、图形、存储和能耗监测。
主要功能
- 01使用 Instruments 分析能耗
- 02配置高效计时器与网络请求
- 03管理定位和后台任务
- 04减少显示、GPU 与磁盘活动
- 05通过 MetricKit 监测能耗
适用场景
- 01比较修改前后的应用能耗
- 02减少轮询和计时器活动
- 03按电池影响调整定位更新
- 04在生产环境跟踪能耗指标
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-energy-ref安装后在 Claude Code 中输入/axiom-energy-ref确认已加载。
详细介绍
能耗优化参考
iOS 能耗优化完整 API 参考,包含 WWDC 讲座和 Apple 文档中的代码示例。
相关技能:axiom-energy(决策树、模式)、axiom-energy-diag(故障排查)
第 1 部分:Power Profiler 工作流
使用 Instruments 录制跟踪记录
连接 Mac 录制
1. Connect iPhone wirelessly to Xcode
- Xcode → Window → Devices and Simulators
- Enable "Connect via network" for your device
2. Profile your app
- Xcode → Product → Profile (Cmd+I)
- Select Blank template
- Click "+" → Add "Power Profiler"
- Optionally add "CPU Profiler" for correlation
3. Record
- Select your app from target dropdown
- Click Record (red button)
- Use app normally for 2-3 minutes
- Click Stop
4. Analyze
- Expand Power Profiler track
- Examine per-app lanes: CPU, GPU, Display, Network
重要:使用无线调试。设备通过线缆充电时,系统能耗显示为 0。
在设备上录制(无需 Mac)
摘自 WWDC25-226:在真实环境中采集跟踪记录。
1. Enable Developer Mode
Settings → Privacy & Security → Developer Mode → Enable
2. Enable Performance Trace
Settings → Developer → Performance Trace → Enable
Set tracing mode to "Power Profiler"
Toggle ON your app in the app list
3. Add Control Center shortcut
Control Center → Tap "+" → Add a Control → Performance Trace
4. Record
Swipe down → Tap Performance Trace icon → Start
Use app (can record up to 10 hours)
Tap Performance Trace icon → Stop
5. Share trace
Settings → Developer → Performance Trace
Tap Share button next to trace file
AirDrop to Mac or email to developer
解读 Power Profiler 指标
| 通道 | 含义 | 高值表示 |
|---|---|---|
| 系统能耗 | 整体电池耗电速率 | 一般能耗 |
| CPU 能耗影响 | 处理器活动评分 | 计算、计时器、解析 |
| GPU 能耗影响 | 图形渲染评分 | 动画、模糊、Metal |
| 显示屏能耗影响 | 屏幕能耗 | 亮度、内容类型 |
| 网络能耗影响 | 无线电活动评分 | 请求、下载、轮询 |
关键要点:这些值是用于比较的评分,而非绝对测量值。请在同一设备上比较优化前后的跟踪记录。
对比优化前后(WWDC25-226 示例)
// Before optimization: CPU Power Impact = 21
VStack {
ForEach(videos) { video in
VideoCardView(video: video)
}
}
// After optimization: CPU Power Impact = 4.3
LazyVStack {
ForEach(videos) { video in
VideoCardView(video: video)
}
}
第 2 部分:计时器效率 API
带容差的 NSTimer
// Basic timer with tolerance
let timer = Timer.scheduledTimer(
withTimeInterval: 1.0,
repeats: true
) { [weak self] _ in
self?.updateUI()
}
timer.tolerance = 0.1 // 10% minimum recommended
// Add to run loop (if not using scheduledTimer)
RunLoop.current.add(timer, forMode: .common)
// Always invalidate when done
deinit {
timer.invalidate()
}
Combine 计时器发布者
import Combine
class ViewModel: ObservableObject {
private var cancellables = Set<AnyCancellable>()
func startPolling() {
Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .default)
.autoconnect()
.sink { [weak self] _ in
self?.refresh()
}
.store(in: &cancellables)
}
func stopPolling() {
cancellables.removeAll()
}
}
Dispatch 计时器源(底层)
摘自《能效指南》:
let queue = DispatchQueue(label: "com.app.timer")
let timer = DispatchSource.makeTimerSource(queue: queue)
// Set interval with leeway (tolerance)
timer.schedule(
deadline: .now(),
repeating: .seconds(1),
leeway: .milliseconds(100) // 10% tolerance
)
timer.setEventHandler { [weak self] in
self?.performWork()
}
timer.resume()
// Cancel when done
timer.cancel()
有关 DispatchSourceTimer 的生命周期安全和崩溃预防,请参阅
axiom-timer-patterns。
计时器的事件驱动替代方案
摘自《能效指南》:优先使用 dispatch 源,而非轮询。
// Monitor file changes instead of polling
let fileDescriptor = open(filePath.path, O_EVTONLY)
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fileDescriptor,
eventMask: [.write, .delete],
queue: .main
)
source.setEventHandler { [weak self] in
self?.handleFileChange()
}
source.setCancelHandler {
close(fileDescriptor)
}
source.resume()
第 3 部分:网络效率 API
URLSession 配置
// Standard configuration with energy-conscious settings
let config = URLSessionConfiguration.default
config.waitsForConnectivity = true // Don't fail immediately
config.allowsExpensiveNetworkAccess = false // Prefer WiFi
config.allowsConstrainedNetworkAccess = false // Respect Low Data Mode
let session = URLSession(configuration: config)
可自由调度的后台下载
摘自 WWDC22-10083:
// Background session for non-urgent downloads
let config = URLSessionConfiguration.background(
withIdentifier: "com.app.downloads"
)
config.isDiscretionary = true // System chooses optimal time
config.sessionSendsLaunchEvents = true
// Set timeouts
config.timeoutIntervalForResource = 24 * 60 * 60 // 24 hours
config.timeoutIntervalForRequest = 60
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
// Create download task with scheduling hints
let task = session.downloadTask(with: url)
task.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60 * 60) // 2 hours from now
task.countOfBytesClientExpectsToSend = 200 // Small request
task.countOfBytesClientExpectsToReceive = 500_000 // 500KB response
task.resume()
后台会话代理
class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL
) {
// Move file from temp location
let destination = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
)[0].appendingPathComponent("downloaded.data")
try? FileManager.default.moveItem(at: location, to: destination)
}
func urlSessionDidFinishEvents(
forBackgroundURLSession session: URLSession
) {
// Notify app delegate to call completion handler
DispatchQueue.main.async {
if let handler = AppDelegate.shared.backgroundCompletionHandler {
handler()
AppDelegate.shared.backgroundCompletionHandler = nil
}
}
}
}
第 4 部分:定位效率 API
CLLocationManager 配置
import CoreLocation
class LocationService: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
func configure() {
manager.delegate = self
// Use appropriate accuracy
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
// Reduce update frequency
manager.distanceFilter = 100 // Update every 100 meters
// Allow indicator pause when stationary
manager.pausesLocationUpdatesAutomatically = true
// For background updates (if needed)
manager.allowsBackgroundLocationUpdates = true
manager.showsBackgroundLocationIndicator = true
}
func startTracking() {
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func startSignificantChangeTracking() {
// Much more energy efficient for background
manager.startMonitoringSignificantLocationChanges()
}
func stopTracking() {
manager.stopUpdatingLocation()
manager.stopMonitoringSignificantLocationChanges()
}
}
iOS 26 及以上版本的 CLLocationUpdate(现代异步 API)
import CoreLocation
func trackLocation() async throws {
for try await update in CLLocationUpdate.liveUpdates() {
// Check if device became stationary
if update.stationary {
// System pauses updates automatically
// Consider switching to region monitoring
break
}
if let location = update.location {
handleLocation(location)
}
}
}
使用 CLMonitor 监听显著变化
import CoreLocation
func setupRegionMonitoring() async {
let monitor = CLMonitor("significant-changes")
// Add condition to monitor
let condition = CLMonitor.CircularGeographicCondition(
center: currentLocation.coordinate,
radius: 500 // 500 meter radius
)
await monitor.add(condition, identifier: "home-region")
// React to events
for try await event in monitor.events {
switch event.state {
case .satisfied:
// Entered region
handleRegionEntry()
case .unsatisfied:
// Exited region
handleRegionExit()
default:
break
}
}
}
定位精度选项
| 常量 | 精度 | 电池影响 | 使用场景 |
|---|---|---|---|
kCLLocationAccuracyBestForNavigation | 约 1m | 极高 | 仅用于逐向导航 |
kCLLocationAccuracyBest | 约 10m | 很高 | 健身追踪 |
kCLLocationAccuracyNearestTenMeters | 约 10m | 高 | 精确定位 |
kCLLocationAccuracyHundredMeters | 约 100m | 中 | 商店定位 |
kCLLocationAccuracyKilometer | 约 1km | 低 | 天气、一般用途 |
kCLLocationAccuracyThreeKilometers | 约 3km | 很低 | 区域内容 |
第 5 部分:后台执行 API
beginBackgroundTask(短时任务)
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTask = application.beginBackgroundTask(withName: "Save State") {
// Expiration handler - clean up
self.endBackgroundTask()
}
// Perform quick work
saveState()
// End immediately when done
endBackgroundTask()
}
private func endBackgroundTask() {
guard backgroundTask != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
BGAppRefreshTask
import BackgroundTasks
// Register at app launch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
return true
}
// Schedule refresh
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 min
try? BGTaskScheduler.shared.submit(request)
}
// Handle refresh
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh() // Schedule next refresh
let fetchTask = Task {
do {
let hasNewData = try await fetchLatestData()
task.setTaskCompleted(success: hasNewData)
} catch {
task.setTaskCompleted(success: false)
}
}
task.expirationHandler = {
fetchTask.cancel()
}
}
BGProcessingTask
import BackgroundTasks
// Register
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.maintenance",
using: nil
) { task in
self.handleMaintenance(task: task as! BGProcessingTask)
}
// Schedule with requirements
func scheduleMaintenance() {
let request = BGProcessingTaskRequest(identifier: "com.app.maintenance")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = true // Only when charging
try? BGTaskScheduler.shared.submit(request)
}
// Handle
func handleMaintenance(task: BGProcessingTask) {
let operation = MaintenanceOperation()
task.expirationHandler = {
operation.cancel()
}
operation.completionBlock = {
task.setTaskCompleted(success: !operation.isCancelled)
}
OperationQueue.main.addOperation(operation)
}
iOS 26 及以上版本的 BGContinuedProcessingTask
摘自 WWDC25-227:通过系统界面继续执行用户发起的任务。
import BackgroundTasks
// Info.plist: Add identifier to BGTaskSchedulerPermittedIdentifiers
// "com.app.export" or "com.app.exports.*" for wildcards
// Register handler (can be dynamic, not just at launch)
func setupExportHandler() {
BGTaskScheduler.shared.register("com.app.export") { task in
let continuedTask = task as! BGContinuedProcessingTask
var shouldContinue = true
continuedTask.expirationHandler = {
shouldContinue = false
}
// Report progress
continuedTask.progress.totalUnitCount = 100
continuedTask.progress.completedUnitCount = 0
// Perform work
for i in 0..<100 {
guard shouldContinue else { break }
performExportStep(i)
continuedTask.progress.completedUnitCount = Int64(i + 1)
}
continuedTask.setTaskCompleted(success: shouldContinue)
}
}
// Submit request
func startExport() {
let request = BGContinuedProcessingTaskRequest(
identifier: "com.app.export",
title: "Exporting Photos",
subtitle: "0 of 100 photos"
)
// Submission strategy
request.strategy = .fail // Fail if can't start immediately
// or default: queue if can't start
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// Handle submission failure
showExportNotAvailable()
}
}
EMRCA 原则(摘自 WWDC25-227)
后台任务必须满足以下原则:
| 原则 | 含义 | 实现方式 |
|---|---|---|
| Efficient(高效) | 轻量、目标明确 | 把一件事做好 |
| Minimal(最少) | 尽量减少工作量 | 不要扩大范围 |
| Resilient(可靠) | 保存进度、处理过期 | 经常创建检查点 |
| Courteous(体谅) | 尊重用户偏好 | 检查低电量模式 |
| Adaptive(适应) | 配合系统工作 | 不要对抗限制 |
第 6 部分:显示屏与 GPU 效率 API
支持深色模式
// Check current appearance
let isDarkMode = traitCollection.userInterfaceStyle == .dark
// React to appearance changes
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
updateColorsForAppearance()
}
}
// Use dynamic colors
let dynamicColor = UIColor { traitCollection in
switch traitCollection.userInterfaceStyle {
case .dark:
return UIColor.black // OLED: True black = pixels off = 0 power
default:
return UIColor.white
}
}
使用 CADisplayLink 控制帧率
摘自 WWDC22-10083:
class AnimationController {
private var displayLink: CADisplayLink?
func startAnimation() {
displayLink = CADisplayLink(target: self, selector: #selector(update))
// Control frame rate
displayLink?.preferredFrameRateRange = CAFrameRateRange(
minimum: 10, // Minimum acceptable
maximum: 30, // Maximum needed
preferred: 30 // Ideal rate
)
displayLink?.add(to: .current, forMode: .default)
}
@objc private func update(_ displayLink: CADisplayLink) {
// Update animation
updateAnimationFrame()
}
func stopAnimation() {
displayLink?.invalidate()
displayLink = nil
}
}
视图不可见时停止动画
class AnimatedViewController: UIViewController {
private var animator: UIViewPropertyAnimator?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startAnimations()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopAnimations() // Critical for energy
}
private func stopAnimations() {
animator?.stopAnimation(true)
animator = nil
}
}
第 7 部分:磁盘 I/O 效率 API
批量写入
// BAD: Multiple small writes
for item in items {
let data = try JSONEncoder().encode(item)
try data.write(to: fileURL) // Writes each item separately
}
// GOOD: Single batched write
let allData = try JSONEncoder().encode(items)
try allData.write(to: fileURL) // One write operation
SQLite WAL 模式
import SQLite3
// Enable Write-Ahead Logging
var db: OpaquePointer?
sqlite3_open(dbPath, &db)
var statement: OpaquePointer?
sqlite3_prepare_v2(db, "PRAGMA journal_mode=WAL", -1, &statement, nil)
sqlite3_step(statement)
sqlite3_finalize(statement)
使用 XCTStorageMetric 进行测试
import XCTest
class DiskWriteTests: XCTestCase {
func testDiskWritePerformance() {
measure(metrics: [XCTStorageMetric()]) {
// Code that writes to disk
saveUserData()
}
}
}
第 8 部分:低电量模式与热状态响应 API
检测低电量模式
import Foundation
class PowerStateManager {
private var cancellables = Set<AnyCancellable>()
init() {
// Check initial state
updateForPowerState()
// Observe changes
NotificationCenter.default.publisher(
for: .NSProcessInfoPowerStateDidChange
)
.sink { [weak self] _ in
self?.updateForPowerState()
}
.store(in: &cancellables)
}
private func updateForPowerState() {
if ProcessInfo.processInfo.isLowPowerModeEnabled {
reduceEnergyUsage()
} else {
restoreNormalOperation()
}
}
private func reduceEnergyUsage() {
// Increase timer intervals
// Reduce animation frame rates
// Defer network requests
// Stop location updates if not critical
// Reduce refresh frequency
}
}
热状态响应
import Foundation
class ThermalManager {
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(thermalStateChanged),
name: ProcessInfo.thermalStateDidChangeNotification,
object: nil
)
}
@objc private func thermalStateChanged() {
switch ProcessInfo.processInfo.thermalState {
case .nominal:
// Normal operation
restoreFullFunctionality()
case .fair:
// Slightly elevated, minor reduction
reduceNonEssentialWork()
case .serious:
// Significant reduction needed
suspendBackgroundTasks()
reduceAnimationQuality()
case .critical:
// Maximum reduction
minimizeAllActivity()
showThermalWarningIfAppropriate()
@unknown default:
break
}
}
}
第 9 部分:MetricKit 监控 API
基本设置
import MetricKit
class MetricsManager: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsManager()
func startMonitoring() {
MXMetricManager.shared.add(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
processPayload(payload)
}
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
processDiagnostic(payload)
}
}
}
处理能耗指标
func processPayload(_ payload: MXMetricPayload) {
// CPU metrics
if let cpu = payload.cpuMetrics {
let foregroundTime = cpu.cumulativeCPUTime
let backgroundTime = cpu.cumulativeCPUInstructions
logMetric("cpu_foreground", value: foregroundTime)
}
// Location metrics
if let location = payload.locationActivityMetrics {
let backgroundLocationTime = location.cumulativeBackgroundLocationTime
logMetric("background_location_seconds", value: backgroundLocationTime)
}
// Network metrics
if let network = payload.networkTransferMetrics {
let cellularUpload = network.cumulativeCellularUpload
let cellularDownload = network.cumulativeCellularDownload
let wifiUpload = network.cumulativeWiFiUpload
let wifiDownload = network.cumulativeWiFiDownload
logMetric("cellular_upload", value: cellularUpload)
logMetric("cellular_download", value: cellularDownload)
}
// Disk metrics
if let disk = payload.diskIOMetrics {
let writes = disk.cumulativeLogicalWrites
logMetric("disk_writes", value: writes)
}
// GPU metrics
if let gpu = payload.gpuMetrics {
let gpuTime = gpu.cumulativeGPUTime
logMetric("gpu_time", value: gpuTime)
}
}
Xcode Organizer 集成
在 Xcode 中查看各项指标:
- 窗口 → Organizer
- 选择你的应用
- 点击侧边栏中的“电池用量”
- 对比不同版本,并按设备/操作系统筛选
显示的类别:
- 音频
- 网络
- 处理(CPU + GPU)
- 显示屏
- Bluetooth
- 位置
- 相机
- 闪光灯
- NFC
- 其他
第 10 部分:推送通知 API
设置提醒通知
摘自 WWDC20-10095:
import UserNotifications
class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
func setup() {
UNUserNotificationCenter.current().delegate = self
UIApplication.shared.registerForRemoteNotifications()
}
func requestPermission() {
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, error in
print("Permission granted: \(granted)")
}
}
}
// AppDelegate
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
sendTokenToServer(token)
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
后台推送通知
// Handle background notification
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
// Check for content-available flag
guard let aps = userInfo["aps"] as? [String: Any],
aps["content-available"] as? Int == 1 else {
completionHandler(.noData)
return
}
Task {
do {
let hasNewData = try await fetchLatestContent()
completionHandler(hasNewData ? .newData : .noData)
} catch {
completionHandler(.failed)
}
}
}
服务端载荷示例
// Alert notification (user-visible)
{
"aps": {
"alert": {
"title": "New Message",
"body": "You have a new message from John"
},
"sound": "default",
"badge": 1
},
"message_id": "12345"
}
// Background notification (silent)
{
"aps": {
"content-available": 1
},
"update_type": "new_content"
}
推送优先级标头
| 优先级 | 标头 | 使用场景 |
|---|---|---|
| 高(10) | apns-priority: 10 | 有时效性的提醒 |
| 低(5) | apns-priority: 5 | 可延迟的更新 |
能耗提示:所有非紧急通知都使用优先级 5。系统会批量处理低优先级推送,以提高能效。
故障排查清单
问题:应用在电池设置中排名靠前
- 运行 Power Profiler,找出主要耗能子系统
- 检查是否存在未设置容差的计时器
- 检查是否存在轮询模式
- 检查是否持续使用定位
- 检查是否存在后台音频会话
- 确认 BGTasks 能及时完成
问题:设备发热
- 检查 GPU Power Impact 是否持续处于较高值
- 检查是否存在持续动画
- 检查动态内容上是否使用了模糊效果
- 确认 Metal 帧率限制已启用
- 检查 CPU 是否存在紧密循环
问题:后台耗电
- 检查 Info.plist 中的后台模式
- 确认停止播放时已停用音频会话
- 检查定位精度,并在不需要时停止定位调用
- 确认 beginBackgroundTask 调用能及时结束
- 检查 BGTask 调度
问题:蜂窝网络用量过高
- 检查 allowsExpensiveNetworkAccess 设置
- 确认后台下载已设置 discretionary 标志
- 检查是否存在轮询模式
- 检查是否存在大型自动下载
专家审核清单
计时器(10 项)
- 所有计时器的容差均 ≥10%
- 在 deinit 中使计时器失效
- 应用进入后台时没有运行中的计时器
- 尽可能使用 Combine Timer
- 没有缺乏合理依据的亚秒级间隔
- 已考虑事件驱动的替代方案
- 不通过计时器轮询进行同步
- 创建新计时器前先使旧计时器失效
- 重复计时器有明确的停止条件
- 后台计时器的使用有合理依据
网络(10 项)
- waitsForConnectivity = true
- allowsExpensiveNetworkAccess 设置适当
- allowsConstrainedNetworkAccess 设置适当
- 非紧急下载使用 discretionary
- 使用推送通知而非轮询
- 尽可能批量处理请求
- 压缩载荷
- 大型传输使用后台 URLSession
- 重试逻辑采用指数退避
- 通过单个 URLSession 重用连接
定位(10 项)
- 精度符合使用场景
- 已设置 distanceFilter
- 不再需要时停止更新
- pausesLocationUpdatesAutomatically = true
- 仅在必要时使用后台定位
- 后台定位使用重大位置变化服务
- 使用 CLMonitor 进行区域监控
- 定位权限与实际需求相符
- 利用静止状态检测
- 向用户说明定位图标的含义
后台执行(10 项)
- 及时调用 endBackgroundTask
- 实现过期处理程序
- 尽可能为 BGTasks 设置 requiresExternalPower
- 遵循 EMRCA 原则
- 仅启用所需的后台模式
- 空闲时停用音频会话
- 增量保存进度
- 任务在时限内完成
- 执行繁重工作前检查低电量模式
- 监控热状态
显示/GPU(10 项)
- 支持深色模式
- 视图隐藏时停止动画
- 帧率适合内容
- 降低次要动画的优先级
- 尽量减少模糊效果
- Metal 限制帧率
- 设计不依赖亮度
- 不存在消耗电量的隐藏动画
- GPU 密集型工作会检查可见性
- 制定帧率决策时考虑 ProMotion
WWDC 会议参考
| 会议 | 年份 | 主题 |
|---|---|---|
| 226 | 2025 | Power Profiler 工作流、设备端跟踪 |
| 227 | 2025 | BGContinuedProcessingTask、EMRCA 原则 |
| 10083 | 2022 | 深色模式、帧率、延迟处理 |
| 10095 | 2020 | 推送通知入门 |
| 707 | 2019 | 后台执行的进展 |
| 417 | 2019 | 电池续航、MetricKit |
最后更新:2025-12-26 平台:iOS 26+、iPadOS 26+
评论
支持 Markdown