axiom-background-processing-diag
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBackground Processing Diagnostics
后台处理故障诊断
Symptom-based troubleshooting for background task issues.
Related skills: (patterns, checklists), (API reference)
axiom-background-processingaxiom-background-processing-ref基于症状的后台任务问题排查方案。
相关技能:(模式、检查清单),(API参考)
axiom-background-processingaxiom-background-processing-refSymptom 1: Task Never Runs
症状1:任务从未运行
Handler never called despite successful .
submit()调用成功,但处理器从未被调用。
submit()Quick Diagnosis (5 minutes)
快速诊断(5分钟)
Task never runs?
│
├─ Step 1: Check Info.plist (2 min)
│ ├─ BGTaskSchedulerPermittedIdentifiers contains EXACT identifier?
│ │ └─ NO → Add identifier, rebuild
│ ├─ UIBackgroundModes includes "fetch" or "processing"?
│ │ └─ NO → Add required mode
│ └─ Identifiers case-sensitive match code?
│ └─ NO → Fix typo, rebuild
│
├─ Step 2: Check registration timing (2 min)
│ ├─ Registered in didFinishLaunchingWithOptions?
│ │ └─ NO → Move registration before return true
│ └─ Registration before first submit()?
│ └─ NO → Ensure register() precedes submit()
│
└─ Step 3: Check app state (1 min)
├─ App swiped away from App Switcher?
│ └─ YES → No background until user opens app
└─ Background App Refresh disabled in Settings?
└─ YES → Enable or inform user任务从未运行?
│
├─ 步骤1:检查Info.plist(2分钟)
│ ├─ BGTaskSchedulerPermittedIdentifiers中是否包含完全匹配的标识符?
│ │ └─ 否 → 添加标识符,重新构建
│ ├─ UIBackgroundModes是否包含"fetch"或"processing"?
│ │ └─ 否 → 添加所需模式
│ └─ 标识符与代码是否大小写完全匹配?
│ └─ 否 → 修正拼写错误,重新构建
│
├─ 步骤2:检查注册时机(2分钟)
│ ├─ 是否在didFinishLaunchingWithOptions中注册?
│ │ └─ 否 → 将注册代码移至return true之前
│ └─ 注册是否在首次调用submit()之前完成?
│ └─ 否 → 确保register()在submit()之前执行
│
└─ 步骤3:检查应用状态(1分钟)
├─ 应用是否从应用切换器中被划走?
│ └─ 是 → 除非用户重新打开应用,否则无法在后台运行
└─ 设置中是否禁用了后台应用刷新?
└─ 是 → 启用该功能或告知用户Time-Cost Analysis
时间成本分析
| Approach | Time | Success Rate |
|---|---|---|
| Check Info.plist + registration | 5 min | 70% (catches most issues) |
| Add console logging | 15 min | 90% |
| LLDB simulate launch | 5 min | 95% (confirms handler works) |
| Random code changes | 2+ hours | Low |
| 方法 | 耗时 | 成功率 |
|---|---|---|
| 检查Info.plist + 注册配置 | 5分钟 | 70%(能排查大多数问题) |
| 添加控制台日志 | 15分钟 | 90% |
| LLDB模拟启动 | 5分钟 | 95%(确认处理器工作正常) |
| 随机修改代码 | 2小时以上 | 低 |
LLDB Quick Test
LLDB快速测试
Verify handler is correctly registered:
lldb
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]If breakpoint hits → Registration correct, issue is scheduling/system factors.
If nothing happens → Registration broken.
验证处理器是否正确注册:
lldb
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]如果触发断点 → 注册正确,问题出在调度/系统因素上。
如果无任何反应 → 注册失败。
Symptom 2: Task Terminates Unexpectedly
症状2:任务意外终止
Handler called but work doesn't complete before termination.
处理器被调用,但工作未完成就终止。
Quick Diagnosis (5 minutes)
快速诊断(5分钟)
Task terminates early?
│
├─ Step 1: Check expiration handler (1 min)
│ ├─ Expiration handler set FIRST in handler?
│ │ └─ NO → Move to very first line
│ └─ Expiration handler actually cancels work?
│ └─ NO → Add cancellation logic
│
├─ Step 2: Check setTaskCompleted (2 min)
│ ├─ Called in success path?
│ ├─ Called in failure path?
│ ├─ Called after expiration?
│ └─ ANY path missing → Task never signals completion
│
├─ Step 3: Check work duration (2 min)
│ ├─ BGAppRefreshTask work > 30 seconds?
│ │ └─ YES → Chunk work or use BGProcessingTask
│ └─ BGProcessingTask work > system limit?
│ └─ YES → Save progress, resume on next launch任务提前终止?
│
├─ 步骤1:检查过期处理器(1分钟)
│ ├─ 是否在处理器的第一行设置过期处理器?
│ │ └─ 否 → 将其移至第一行
│ └─ 过期处理器是否实际执行了取消工作的逻辑?
│ └─ 否 → 添加取消逻辑
│
├─ 步骤2:检查setTaskCompleted调用(2分钟)
│ ├─ 成功路径中是否调用了该方法?
│ ├─ 失败路径中是否调用了该方法?
│ ├─ 过期后是否调用了该方法?
│ └─ 任何路径未调用 → 任务从未发出完成信号
│
├─ 步骤3:检查工作时长(2分钟)
│ ├─ BGAppRefreshTask的工作时长是否超过30秒?
│ │ └─ 是 → 拆分工作或使用BGProcessingTask
│ └─ BGProcessingTask的工作时长是否超过系统限制?
│ └─ 是 → 保存进度,下次启动时恢复Common Causes
常见原因
| Cause | Fix |
|---|---|
| Missing expiration handler | Set handler as first line |
| setTaskCompleted not called | Add to ALL code paths |
| Work takes too long | Chunk and checkpoint |
| Network timeout > task time | Use background URLSession |
| Async callback after expiration | Check shouldContinue flag |
| 原因 | 修复方案 |
|---|---|
| 缺少过期处理器 | 将处理器设置为第一行代码 |
| 未调用setTaskCompleted | 在所有代码路径中添加调用 |
| 工作耗时过长 | 拆分任务并设置检查点 |
| 网络超时超过任务时长 | 使用后台URLSession |
| 过期后执行异步回调 | 检查shouldContinue标志 |
Test Expiration Handling
测试过期处理逻辑
lldb
// First simulate launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]
// Then force expiration
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.yourapp.refresh"]Verify expiration handler runs and work stops gracefully.
lldb
// 首先模拟启动
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]
// 然后强制触发过期
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.yourapp.refresh"]验证过期处理器是否运行,且工作是否优雅停止。
Symptom 3: Background URLSession Delegate Not Called
症状3:Background URLSession代理未被调用
Download completes but never fires.
didFinishDownloadingTo下载完成,但从未触发。
didFinishDownloadingToQuick Diagnosis (5 minutes)
快速诊断(5分钟)
URLSession delegate not called?
│
├─ Step 1: Check session configuration (2 min)
│ ├─ Using URLSessionConfiguration.background()?
│ │ └─ NO → Must use background config
│ ├─ Session identifier unique?
│ │ └─ NO → Use unique bundle-prefixed ID
│ └─ sessionSendsLaunchEvents = true?
│ └─ NO → Set for app relaunch on completion
│
├─ Step 2: Check AppDelegate handler (2 min)
│ ├─ handleEventsForBackgroundURLSession implemented?
│ │ └─ NO → Required for session events
│ └─ Completion handler stored and called later?
│ └─ NO → Store handler, call after events processed
│
└─ Step 3: Check delegate assignment (1 min)
├─ Session created with delegate?
└─ Delegate not nil when task completes?URLSession代理未被调用?
│
├─ 步骤1:检查会话配置(2分钟)
│ ├─ 是否使用URLSessionConfiguration.background()?
│ │ └─ 否 → 必须使用后台配置
│ ├─ 会话标识符是否唯一?
│ │ └─ 否 → 使用带Bundle前缀的唯一ID
│ └─ sessionSendsLaunchEvents是否设为true?
│ └─ 否 → 设置为true以在完成时重启应用
│
├─ 步骤2:检查AppDelegate处理器(2分钟)
│ ├─ 是否实现了handleEventsForBackgroundURLSession?
│ │ └─ 否 → 该方法是处理会话事件的必需方法
│ └─ 是否保存并在后续调用了完成处理器?
│ └─ 否 → 保存处理器,处理完事件后再调用
│
└─ 步骤3:检查代理赋值(1分钟)
├─ 创建会话时是否指定了代理?
└─ 任务完成时代理是否不为nil?Required AppDelegate Code
必需的AppDelegate代码
swift
// Store completion handler
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
// Call after all events processed
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.backgroundSessionCompletionHandler?()
self.backgroundSessionCompletionHandler = nil
}
}swift
// 保存完成处理器
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
// 处理完所有事件后调用
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.backgroundSessionCompletionHandler?()
self.backgroundSessionCompletionHandler = nil
}
}Symptom 4: Works in Development, Not Production
症状4:开发环境正常,生产环境异常
Task runs with debugger but fails in release builds or for users.
任务在调试器中运行正常,但在发布版本或用户设备上失败。
Quick Diagnosis (10 minutes)
快速诊断(10分钟)
Works in dev, not prod?
│
├─ Step 1: Check system constraints (3 min)
│ ├─ Low Power Mode enabled?
│ │ └─ Check ProcessInfo.isLowPowerModeEnabled
│ ├─ Background App Refresh disabled?
│ │ └─ Check UIApplication.backgroundRefreshStatus
│ └─ Battery < 20%?
│ └─ System pauses discretionary work
│
├─ Step 2: Check app state (2 min)
│ ├─ App force-quit from App Switcher?
│ │ └─ YES → No background until foreground launch
│ └─ App recently used?
│ └─ Rarely used apps get lower priority
│
├─ Step 3: Check build differences (3 min)
│ ├─ Debug vs Release optimization differences?
│ ├─ #if DEBUG code excluding production?
│ └─ Different bundle identifier in release?
│
└─ Step 4: Add production logging (2 min)
└─ Log task schedule/launch/complete to analytics开发环境正常,生产环境异常?
│
├─ 步骤1:检查系统限制(3分钟)
│ ├─ 是否启用了低电量模式?
│ │ └─ 检查ProcessInfo.isLowPowerModeEnabled
│ ├─ 是否禁用了后台应用刷新?
│ │ └─ 检查UIApplication.backgroundRefreshStatus
│ └─ 电量是否低于20%?
│ └─ 系统会暂停可自由支配的工作
│
├─ 步骤2:检查应用状态(2分钟)
│ ├─ 应用是否从应用切换器中被强制退出?
│ │ └─ 是 → 除非应用前台启动,否则无法在后台运行
│ └─ 应用是否被用户频繁使用?
│ └─ 很少使用的应用优先级更低
│
├─ 步骤3:检查构建差异(3分钟)
│ ├─ Debug与Release版本的优化设置是否不同?
│ ├─ 是否有#if DEBUG代码在生产环境中被排除?
│ └─ 发布版本的Bundle标识符是否不同?
│
└─ 步骤4:添加生产环境日志(2分钟)
└─ 将任务的调度/启动/完成情况记录到分析工具中The 7 Scheduling Factors
7大调度影响因素
All affect task execution in production:
| Factor | Check |
|---|---|
| Critically Low Battery | Battery < 20%? |
| Low Power Mode | ProcessInfo.isLowPowerModeEnabled |
| App Usage | User opens app frequently? |
| App Switcher | App NOT swiped away? |
| Background App Refresh | Settings enabled? |
| System Budgets | Many recent background launches? |
| Rate Limiting | Requests too frequent? |
所有因素都会影响生产环境中的任务执行:
| 因素 | 检查项 |
|---|---|
| 电量极低 | 电量是否低于20%? |
| 低电量模式 | ProcessInfo.isLowPowerModeEnabled |
| 应用使用频率 | 用户是否频繁打开应用? |
| 应用切换器状态 | 应用是否未被划走? |
| 后台应用刷新 | 设置中是否启用? |
| 系统预算 | 近期是否有大量后台启动? |
| 速率限制 | 请求是否过于频繁? |
Production Debugging
生产环境调试
Add logging to track what's happening:
swift
func scheduleRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
do {
try BGTaskScheduler.shared.submit(request)
Analytics.log("background_task_scheduled")
} catch {
Analytics.log("background_task_schedule_failed", error: error)
}
}
func handleRefresh(task: BGAppRefreshTask) {
Analytics.log("background_task_started")
// ... work ...
Analytics.log("background_task_completed")
task.setTaskCompleted(success: true)
}添加日志以跟踪运行情况:
swift
func scheduleRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
do {
try BGTaskScheduler.shared.submit(request)
Analytics.log("background_task_scheduled")
} catch {
Analytics.log("background_task_schedule_failed", error: error)
}
}
func handleRefresh(task: BGAppRefreshTask) {
Analytics.log("background_task_started")
// ... 执行工作 ...
Analytics.log("background_task_completed")
task.setTaskCompleted(success: true)
}Symptom 5: Inconsistent Task Scheduling
症状5:任务调度不一致
Task runs sometimes but not predictably.
任务有时运行,有时不运行,无规律可循。
Quick Diagnosis (5 minutes)
快速诊断(5分钟)
Inconsistent scheduling?
│
├─ Step 1: Understand earliestBeginDate (2 min)
│ ├─ This is MINIMUM delay, not scheduled time
│ │ └─ System runs when convenient AFTER this date
│ └─ Set too far in future (> 1 week)?
│ └─ System may skip task entirely
│
├─ Step 2: Check scheduling pattern (2 min)
│ ├─ Scheduling same task multiple times?
│ │ └─ Call getPendingTaskRequests to check
│ └─ Scheduling in handler for continuity?
│ └─ Required for continuous refresh
│
└─ Step 3: Understand system behavior (1 min)
├─ BGAppRefreshTask runs based on USER patterns
│ └─ User rarely opens app = rare runs
└─ BGProcessingTask runs when charging
└─ User doesn't charge overnight = no runs调度不一致?
│
├─ 步骤1:理解earliestBeginDate(2分钟)
│ ├─ 这是最小延迟时间,而非固定调度时间
│ │ └─ 系统会在该时间之后的合适时机运行任务
│ └─ 是否设置为过远的未来时间(超过1周)?
│ └─ 是 → 系统可能会完全跳过该任务
│
├─ 步骤2:检查调度逻辑(2分钟)
│ ├─ 是否多次调度同一个任务?
│ │ └─ 调用getPendingTaskRequests检查
│ └─ 是否在处理器中调度任务以保持连续性?
│ └─ 是 → 这是实现持续刷新的必需操作
│
└─ 步骤3:理解系统行为(1分钟)
├─ BGAppRefreshTask基于用户使用模式运行
│ └─ 用户很少打开应用 → 任务运行频率低
└─ BGProcessingTask在充电时运行
└─ 用户夜间不充电 → 任务不运行Expected Behavior
预期行为
| Task Type | Scheduling Behavior |
|---|---|
| BGAppRefreshTask | Runs before predicted app usage times |
| BGProcessingTask | Runs when charging + idle (typically overnight) |
| Silent Push | Rate-limited; 14 pushes may = 7 launches |
Key insight: You request a time window. System decides when (or if) to run.
| 任务类型 | 调度行为 |
|---|---|
| BGAppRefreshTask | 在预测的用户打开应用时间之前运行 |
| BGProcessingTask | 在充电且设备空闲时运行(通常是夜间) |
| 静默推送 | 受速率限制;14次推送可能仅触发7次启动 |
核心要点:你请求的是一个时间窗口,系统决定何时(或是否)运行任务。
Symptom 6: App Crashes on Background Launch
症状6:应用在后台启动时崩溃
App crashes when launched by system for background task.
系统为执行后台任务而启动应用时,应用崩溃。
Quick Diagnosis (5 minutes)
快速诊断(5分钟)
Crash on background launch?
│
├─ Step 1: Check launch initialization (2 min)
│ ├─ UI setup before task handler?
│ │ └─ Background launch may not have UI context
│ ├─ Accessing files before first unlock?
│ │ └─ Use completeUntilFirstUserAuthentication protection
│ └─ Force unwrapping optionals that may be nil?
│ └─ Guard against nil in background context
│
├─ Step 2: Check handler safety (2 min)
│ ├─ Handler captures self strongly?
│ │ └─ Use [weak self] to prevent retain cycles
│ └─ Handler accesses UI on non-main thread?
│ └─ Dispatch UI work to main queue
│
└─ Step 3: Check data protection (1 min)
└─ Files accessible when device locked?
└─ Use .completeUnlessOpen or .completeUntilFirstUserAuthentication后台启动时崩溃?
│
├─ 步骤1:检查启动初始化逻辑(2分钟)
│ ├─ 是否在任务处理器之前进行UI设置?
│ │ └─ 后台启动可能没有UI上下文
│ ├─ 是否在设备首次解锁前访问文件?
│ │ └─ 使用completeUntilFirstUserAuthentication保护级别
│ └─ 是否强制解包可能为nil的可选值?
│ └─ 在后台上下文中要防范nil值
│
├─ 步骤2:检查处理器安全性(2分钟)
│ ├─ 处理器是否强引用self?
│ │ └─ 使用[weak self]避免循环引用
│ └─ 处理器是否在非主线程访问UI?
│ └─ 将UI工作调度到主队列
│
└─ 步骤3:检查数据保护(1分钟)
└─ 设备锁定时文件是否可访问?
└─ 使用.completeUnlessOpen或.completeUntilFirstUserAuthenticationFile Protection for Background Tasks
后台任务的文件保护设置
swift
// Set appropriate protection when creating files
try data.write(to: url, options: .completeFileProtectionUntilFirstUserAuthentication)
// Or configure in entitlements for entire appswift
// 创建文件时设置合适的保护级别
try data.write(to: url, options: .completeFileProtectionUntilFirstUserAuthentication)
// 或在 entitlements 中为整个应用配置Safe Handler Pattern
安全的处理器模式
swift
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { [weak self] task in
guard let self = self else {
task.setTaskCompleted(success: false)
return
}
// Don't access UI
// Use background-safe APIs only
self.performBackgroundWork(task: task)
}swift
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { [weak self] task in
guard let self = self else {
task.setTaskCompleted(success: false)
return
}
// 不要访问UI
// 仅使用后台安全的API
self.performBackgroundWork(task: task)
}Symptom 7: Task Runs Multiple Times
症状7:任务多次运行
Same task appears to run repeatedly or in parallel.
同一个任务似乎重复运行或并行运行。
Quick Diagnosis (5 minutes)
快速诊断(5分钟)
Task runs multiple times?
│
├─ Step 1: Check scheduling logic (2 min)
│ ├─ Scheduling on every app launch?
│ │ └─ Check getPendingTaskRequests first
│ ├─ Scheduling in handler AND elsewhere?
│ │ └─ Consolidate to single location
│ └─ Using same identifier for different purposes?
│ └─ Use unique identifiers per task type
│
├─ Step 2: Check for duplicate submissions (2 min)
│ └─ Multiple submit() calls queued?
│ └─ System may batch into single execution
│
└─ Step 3: Check handler execution (1 min)
└─ setTaskCompleted called promptly?
└─ Delay may cause system to think task hung任务多次运行?
│
├─ 步骤1:检查调度逻辑(2分钟)
│ ├─ 是否每次应用启动都调度任务?
│ │ └─ 先调用getPendingTaskRequests检查
│ ├─ 是否同时在处理器和其他地方调度任务?
│ │ └─ 统一到单一位置调度
│ └─ 是否将同一标识符用于不同用途?
│ └─ 为不同类型的任务使用唯一标识符
│
├─ 步骤2:检查重复提交(2分钟)
│ └─ 是否有多个submit()调用被排队?
│ └─ 系统可能会将其合并为单次执行
│
└─ 步骤3:检查处理器执行情况(1分钟)
└─ 是否及时调用了setTaskCompleted?
└─ 延迟调用可能导致系统认为任务挂起Prevent Duplicate Scheduling
防止重复调度
swift
func scheduleRefreshIfNeeded() {
BGTaskScheduler.shared.getPendingTaskRequests { requests in
let alreadyScheduled = requests.contains {
$0.identifier == "com.app.refresh"
}
if !alreadyScheduled {
self.scheduleRefresh()
}
}
}swift
func scheduleRefreshIfNeeded() {
BGTaskScheduler.shared.getPendingTaskRequests { requests in
let alreadyScheduled = requests.contains {
$0.identifier == "com.app.refresh"
}
if !alreadyScheduled {
self.scheduleRefresh()
}
}
}Quick Diagnostic Checklist
快速诊断检查清单
30-Second Check
30秒快速检查
- Info.plist has identifier?
- Registration in didFinishLaunchingWithOptions?
- App not swiped away?
- Info.plist中是否包含标识符?
- 是否在didFinishLaunchingWithOptions中注册?
- 应用是否未被划走?
5-Minute Check
5分钟检查
- Identifiers exactly match (case-sensitive)?
- Background mode enabled (fetch/processing)?
- setTaskCompleted called in all paths?
- Expiration handler set first?
- 标识符是否完全匹配(区分大小写)?
- 是否启用了后台模式(fetch/processing)?
- 是否在所有路径中调用了setTaskCompleted?
- 是否在第一行设置了过期处理器?
15-Minute Investigation
15分钟深入排查
- LLDB simulate launch works?
- LLDB simulate expiration handled?
- Console shows registration/scheduling logs?
- Real device (not just simulator)?
- Release build (not just debug)?
- Background App Refresh enabled in Settings?
- LLDB模拟启动是否正常?
- LLDB模拟过期是否能正确处理?
- 控制台是否显示注册/调度日志?
- 是否在真实设备上测试(而非仅模拟器)?
- 是否测试了发布版本(而非仅Debug版本)?
- 设置中是否启用了后台应用刷新?
Console Log Filters
控制台日志过滤器
// All background task events
subsystem:com.apple.backgroundtaskscheduler
// Specific to your app
subsystem:com.apple.backgroundtaskscheduler message:"com.yourapp"// 所有后台任务事件
subsystem:com.apple.backgroundtaskscheduler
// 仅针对你的应用
subsystem:com.apple.backgroundtaskscheduler message:"com.yourapp"Expected Log Sequence
预期日志序列
- "Registered handler for task with identifier"
- "Scheduling task with identifier"
- "Starting task with identifier"
- (your work executes)
- "Task completed with identifier"
Missing any step = issue at that stage.
- "Registered handler for task with identifier"
- "Scheduling task with identifier"
- "Starting task with identifier"
- (你的工作执行)
- "Task completed with identifier"
缺少任何步骤 → 该阶段存在问题。
Resources
参考资源
WWDC: 2019-707 (debugging commands), 2020-10063 (7 factors)
Skills: axiom-background-processing, axiom-background-processing-ref
Last Updated: 2025-12-31
Platforms: iOS 13+
WWDC:2019-707(调试命令),2020-10063(7大影响因素)
技能:axiom-background-processing, axiom-background-processing-ref
最后更新:2025-12-31
支持平台:iOS 13+