mcpField
A
axiom-energy-ref

axiom-energy-ref

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

关于 axiom-energy-ref

这是一份降低 iOS 应用能耗的 API 参考,结合 Apple 文档和 WWDC 示例,涵盖能耗分析以及计时器、网络、定位、后台任务、图形、存储和能耗监测。

主要功能

  1. 01使用 Instruments 分析能耗
  2. 02配置高效计时器与网络请求
  3. 03管理定位和后台任务
  4. 04减少显示、GPU 与磁盘活动
  5. 05通过 MetricKit 监测能耗

适用场景

  1. 01比较修改前后的应用能耗
  2. 02减少轮询和计时器活动
  3. 03按电池影响调整定位更新
  4. 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 中查看各项指标:

  1. 窗口 → Organizer
  2. 选择你的应用
  3. 点击侧边栏中的“电池用量”
  4. 对比不同版本,并按设备/操作系统筛选

显示的类别:

  • 音频
  • 网络
  • 处理(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 会议参考

会议年份主题
2262025Power Profiler 工作流、设备端跟踪
2272025BGContinuedProcessingTask、EMRCA 原则
100832022深色模式、帧率、延迟处理
100952020推送通知入门
7072019后台执行的进展
4172019电池续航、MetricKit

最后更新:2025-12-26 平台:iOS 26+、iPadOS 26+

评论

支持 Markdown