watchOS后台任务调用setTaskCompleted时崩溃问题求助
setTaskCompleted with WCSession KVO Let’s walk through the likely causes of this crash and how to fix them, since your setup involves WCSession KVO in the ExtensionDelegate’s init method.
Common Causes & Fixes
1. Missing KVO Observer Cleanup
When you add KVO observers in init, you must remove them before the ExtensionDelegate instance is deallocated. Watch Extensions have a tight lifecycle—if the extension is suspended or terminated and the observers aren’t removed, subsequent WCSession state changes will send notifications to a deallocated object, which almost always causes a crash (especially when interacting with background tasks).
Add a deinit method to your ExtensionDelegate to clean up the observers:
deinit { guard WCSession.isSupported() else { return } let session = WCSession.default // Match the exact key paths and context used when adding observers session.removeObserver(self, forKeyPath: "activationState", context: &ExtensionDelegate.wcSessionKVOcontext) session.removeObserver(self, forKeyPath: "hasContentPending", context: &ExtensionDelegate.wcSessionKVOcontext) }
Important: Never use nil for the context parameter when removing observers—this can accidentally remove observers added by parent classes or other parts of your code, leading to unexpected behavior.
2. Timing Conflicts Between WCSession KVO and Background Tasks
WCSession state changes (like activationState or hasContentPending) might fire after you’ve marked a background task as completed. If your KVO callback attempts to interact with the already-completed task (or the extension is in a deallocation state), this can trigger a crash.
Fix this by:
- Ensuring you remove KVO observers before calling
setTaskCompleted(). For example, if your background task handles WCSession content, clean up observers once the content is processed and before marking the task as done. - Adding a guard clause in your KVO callback to check if the background task is still active before performing any operations.
3. Duplicate Calls to setTaskCompleted
If your KVO callbacks accidentally trigger multiple calls to setTaskCompleted() for the same background task, this will cause a crash. WatchKit strictly enforces that each background task is completed exactly once.
Prevent this by:
- Using a private boolean property to track whether the task has already been completed:
private var isTaskCompleted = false func completeBackgroundTask(_ task: WKBackgroundTask) { guard !isTaskCompleted else { return } isTaskCompleted = true task.setTaskCompleted() } - Checking the task’s state (if possible) before calling
setTaskCompleted().
4. Accidental Deallocation of ExtensionDelegate
If your ExtensionDelegate instance is deallocated prematurely (e.g., due to incorrect memory management), the KVO notifications will target a dangling pointer. To confirm this, add a print statement in deinit to see if it’s being called before you expect:
deinit { print("ExtensionDelegate deallocated") // ... observer cleanup code }
If you see this print before your background task completes, you’ll need to adjust your memory management to keep the delegate alive until the task is done.
Recommended Best Practices
- Prefer WCSession Delegates Over KVO: Watch Connectivity provides delegate methods like
session(_:activationDidCompleteWith:error:),sessionDidBecomeInactive(_:), andsessionHasContentPending(_:)that are designed to handle state changes without the risk of KVO memory leaks. This is a more robust approach than KVO for Watch Extensions. - Delay KVO Setup: Instead of adding observers in
init, wait until the extension finishes launching (e.g., inapplicationDidFinishLaunching). This ensures the session is properly initialized before observing it. - Tighten Background Task Scope: Keep background task logic focused—process the required content, clean up resources (like KVO observers), and mark the task as completed as soon as possible.
内容的提问来源于stack exchange,提问作者Reinhard Männer

