watchOS 后台任务在 setTaskCompleted 上崩溃
watchOS background task crashes on setTaskCompleted
在我的 watch extension delegate init
函数中,我在 WCSession
上设置了 KVO 观察者:
if WCSession.isSupported() {
let defaultSession = WCSession.default
defaultSession.addObserver(self, forKeyPath: "activationState",
options: [.old, .new],
context: &ExtensionDelegate.wcSessionKVOcontext)
defaultSession.addObserver(self, forKeyPath: "hasContentPending",
options: [.old, .new],
context: &ExtensionDelegate.wcSessionKVOcontext)
}
为了完成所有watch后台任务,这里调用函数
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &ExtensionDelegate.wcSessionKVOcontext {
printStatusChanges(keyPath: keyPath, change: change)
DispatchQueue.main.async {
self.completeAllTasksIfReady()
}
}
}
和
private func completeAllTasksIfReady() {
let session = WCSession.default
// the session's properties only have valid values if the session is activated, so check that first
if session.activationState == .activated && !session.hasContentPending {
if wcBackgroundTasks.isEmpty {
print("No background tasks")
} else {
wcBackgroundTasks.forEach { [=12=].setTaskCompleted() }
print("\(wcBackgroundTasks.count) connectivity background tasks completed")
}
wcBackgroundTasks.removeAll()
}
}
通常情况下,这可以正常工作。
但是我遇到了以下日志的崩溃:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x1db2eda8 __abort_with_payload + 24
1 libsystem_kernel.dylib 0x1db2ae60 abort_with_payload_wrapper_internal + 60
2 libsystem_kernel.dylib 0x1db2ae24 abort_with_payload_wrapper_internal + 0
3 WatchKit 0x2ddfce86 -[WKRefreshBackgroundTask setTaskCompleted] + 414
4 Watch Extension 0x00037258 closure #1 in ExtensionDelegate.completeAllTasksIfReady() + 209496 (ExtensionDelegate.swift:196)
5 Watch Extension 0x000372b0 thunk for @callee_guaranteed (@owned WKRefreshBackgroundTask) -> (@error @owned Error) + 209584 (ExtensionDelegate.swift:0)
6 Watch Extension 0x0003af08 partial apply for thunk for @callee_guaranteed (@owned WKRefreshBackgroundTask) -> (@error @owned Error) + 225032 (ExtensionDelegate.swift:0)
7 libswiftCore.dylib 0x00525350 0x2fc000 + 2265936
8 libswiftCore.dylib 0x00433b7c 0x2fc000 + 1276796
9 libswiftCore.dylib 0x00304060 0x2fc000 + 32864
10 Watch Extension 0x00036eec ExtensionDelegate.completeAllTasksIfReady() + 208620 (ExtensionDelegate.swift:196)
11 Watch Extension 0x000350d4 closure #1 in ExtensionDelegate.observeValue(forKeyPath:of:change:context:) + 200916 (ExtensionDelegate.swift:108)
12 Watch Extension 0x0000993c _T0Ieg_IeyB_TR + 22844 (AlertManager.swift:0)
13 libdispatch.dylib 0x1d9d3456 _dispatch_call_block_and_release + 10
14 libdispatch.dylib 0x1d9d3432 _dispatch_client_callout + 6
15 libdispatch.dylib 0x1d9e3604 _dispatch_main_queue_callback_4CF$VARIANT$mp + 858
16 CoreFoundation 0x1df7db1e __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 10
17 CoreFoundation 0x1df7b73c __CFRunLoopRun + 932
18 CoreFoundation 0x1dec7660 CFRunLoopRunSpecific + 534
19 GraphicsServices 0x1fb7ab3e GSEventRunModal + 94
20 UIKit 0x24746604 UIApplicationMain + 156
21 libxpc.dylib 0x1dcf7b14 _xpc_objc_main + 586
22 libxpc.dylib 0x1dcf94a8 xpc_main + 154
23 Foundation 0x1e9b2cf2 service_connection_handler + 0
24 PlugInKit 0x2525c06e -[PKService run] + 676
25 WatchKit 0x2de1b036 main + 162
26 libdyld.dylib 0x1da2e782 start + 2
显然,当在 WKRefreshBackgroundTask
任务之一中调用 setTaskCompleted
时会发生崩溃。
但是可能是什么原因,或者如何调试呢?
我的错。 Apple 回复了我的错误报告:
This issue behaves as intended based on the following:
- this is an assert put in place to make sure that the same task is not completed more than once
- this is indicated by "called on WKWatchConnectivityRefreshBackgroundTask: 0x16e7b230
that's already
been completed”
- this likely points to a bug (or bugs) in the project’s code and not with the SDK/API
因此,我仔细检查了我的代码中是否有可能多次调用 WKWatchConnectivityRefreshBackgroundTask
函数 setTaskCompleted
。确实是这样:
当我开始开发手表扩展时,我复制了Apple demo code。它包含以下功能:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for backgroundTask in backgroundTasks {
if let wcBackgroundTask = backgroundTask as? WKWatchConnectivityRefreshBackgroundTask {
// store a reference to the task objects as we might have to wait to complete them
self.wcBackgroundTasks.append(wcBackgroundTask)
} else {
// immediately complete all other task types as we have not added support for them
backgroundTask.setTaskCompleted()
}
}
completeAllTasksIfReady()
}
这里,一个应该处理的WKWatchConnectivityRefreshBackgroundTask
存储在一个数组wcBackgroundTasks
中。稍后在 completeAllTasksIfReady()
中,此数组中的所有任务都由 wcBackgroundTasks.forEach { [=17=].setTaskCompleted() }
完成。
我的错是我将我的 WKWatchConnectivityRefreshBackgroundTask
存储在这个数组中(稍后调用 setTaskCompleted
)并立即处理它并设置 setTaskCompleted
。所以这被调用了两次。
感谢发现这个的 Apple 工程师。
在我的 watch extension delegate init
函数中,我在 WCSession
上设置了 KVO 观察者:
if WCSession.isSupported() {
let defaultSession = WCSession.default
defaultSession.addObserver(self, forKeyPath: "activationState",
options: [.old, .new],
context: &ExtensionDelegate.wcSessionKVOcontext)
defaultSession.addObserver(self, forKeyPath: "hasContentPending",
options: [.old, .new],
context: &ExtensionDelegate.wcSessionKVOcontext)
}
为了完成所有watch后台任务,这里调用函数
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &ExtensionDelegate.wcSessionKVOcontext {
printStatusChanges(keyPath: keyPath, change: change)
DispatchQueue.main.async {
self.completeAllTasksIfReady()
}
}
}
和
private func completeAllTasksIfReady() {
let session = WCSession.default
// the session's properties only have valid values if the session is activated, so check that first
if session.activationState == .activated && !session.hasContentPending {
if wcBackgroundTasks.isEmpty {
print("No background tasks")
} else {
wcBackgroundTasks.forEach { [=12=].setTaskCompleted() }
print("\(wcBackgroundTasks.count) connectivity background tasks completed")
}
wcBackgroundTasks.removeAll()
}
}
通常情况下,这可以正常工作。
但是我遇到了以下日志的崩溃:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x1db2eda8 __abort_with_payload + 24
1 libsystem_kernel.dylib 0x1db2ae60 abort_with_payload_wrapper_internal + 60
2 libsystem_kernel.dylib 0x1db2ae24 abort_with_payload_wrapper_internal + 0
3 WatchKit 0x2ddfce86 -[WKRefreshBackgroundTask setTaskCompleted] + 414
4 Watch Extension 0x00037258 closure #1 in ExtensionDelegate.completeAllTasksIfReady() + 209496 (ExtensionDelegate.swift:196)
5 Watch Extension 0x000372b0 thunk for @callee_guaranteed (@owned WKRefreshBackgroundTask) -> (@error @owned Error) + 209584 (ExtensionDelegate.swift:0)
6 Watch Extension 0x0003af08 partial apply for thunk for @callee_guaranteed (@owned WKRefreshBackgroundTask) -> (@error @owned Error) + 225032 (ExtensionDelegate.swift:0)
7 libswiftCore.dylib 0x00525350 0x2fc000 + 2265936
8 libswiftCore.dylib 0x00433b7c 0x2fc000 + 1276796
9 libswiftCore.dylib 0x00304060 0x2fc000 + 32864
10 Watch Extension 0x00036eec ExtensionDelegate.completeAllTasksIfReady() + 208620 (ExtensionDelegate.swift:196)
11 Watch Extension 0x000350d4 closure #1 in ExtensionDelegate.observeValue(forKeyPath:of:change:context:) + 200916 (ExtensionDelegate.swift:108)
12 Watch Extension 0x0000993c _T0Ieg_IeyB_TR + 22844 (AlertManager.swift:0)
13 libdispatch.dylib 0x1d9d3456 _dispatch_call_block_and_release + 10
14 libdispatch.dylib 0x1d9d3432 _dispatch_client_callout + 6
15 libdispatch.dylib 0x1d9e3604 _dispatch_main_queue_callback_4CF$VARIANT$mp + 858
16 CoreFoundation 0x1df7db1e __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 10
17 CoreFoundation 0x1df7b73c __CFRunLoopRun + 932
18 CoreFoundation 0x1dec7660 CFRunLoopRunSpecific + 534
19 GraphicsServices 0x1fb7ab3e GSEventRunModal + 94
20 UIKit 0x24746604 UIApplicationMain + 156
21 libxpc.dylib 0x1dcf7b14 _xpc_objc_main + 586
22 libxpc.dylib 0x1dcf94a8 xpc_main + 154
23 Foundation 0x1e9b2cf2 service_connection_handler + 0
24 PlugInKit 0x2525c06e -[PKService run] + 676
25 WatchKit 0x2de1b036 main + 162
26 libdyld.dylib 0x1da2e782 start + 2
显然,当在 WKRefreshBackgroundTask
任务之一中调用 setTaskCompleted
时会发生崩溃。
但是可能是什么原因,或者如何调试呢?
我的错。 Apple 回复了我的错误报告:
This issue behaves as intended based on the following:
- this is an assert put in place to make sure that the same task is not completed more than once
- this is indicated by "called onWKWatchConnectivityRefreshBackgroundTask: 0x16e7b230
that's already been completed”
- this likely points to a bug (or bugs) in the project’s code and not with the SDK/API
因此,我仔细检查了我的代码中是否有可能多次调用 WKWatchConnectivityRefreshBackgroundTask
函数 setTaskCompleted
。确实是这样:
当我开始开发手表扩展时,我复制了Apple demo code。它包含以下功能:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for backgroundTask in backgroundTasks {
if let wcBackgroundTask = backgroundTask as? WKWatchConnectivityRefreshBackgroundTask {
// store a reference to the task objects as we might have to wait to complete them
self.wcBackgroundTasks.append(wcBackgroundTask)
} else {
// immediately complete all other task types as we have not added support for them
backgroundTask.setTaskCompleted()
}
}
completeAllTasksIfReady()
}
这里,一个应该处理的WKWatchConnectivityRefreshBackgroundTask
存储在一个数组wcBackgroundTasks
中。稍后在 completeAllTasksIfReady()
中,此数组中的所有任务都由 wcBackgroundTasks.forEach { [=17=].setTaskCompleted() }
完成。
我的错是我将我的 WKWatchConnectivityRefreshBackgroundTask
存储在这个数组中(稍后调用 setTaskCompleted
)并立即处理它并设置 setTaskCompleted
。所以这被调用了两次。
感谢发现这个的 Apple 工程师。