如何诊断和解决 WCSession sendMessage(_:replyHandler:errorHandler:) 上的崩溃?
How can I diagnose and resolve a crash on WCSession sendMessage(_:replyHandler:errorHandler:)?
我正在构建一个 watchOS 应用程序,它需要定期从配套 iPhone 应用程序请求信息以刷新复杂功能。
为此,我有一个定期运行的 WKApplicationRefreshBackgroundTask
。它使用 WatchConnectivity
中的 sendMessage(_:replyHandler:errorHandler:)
从 iPhone 应用请求信息,处理回复,并更新并发症。
大多数情况下,这对许多用户来说都是可靠的,但是,我看到 Apple Watch 在与 sendMessage(_:replyHandler:errorHandler:)
相关的 Xcode 中崩溃,我担心这会导致用户错过并发症更新.一些用户一直在抱怨复杂功能没有更新,所以我试图确定是否存在超出 watchOS 中复杂功能刷新频率的常规限制的问题。
有人对我如何解决这个问题有什么建议吗?或者,你对我如何更好地解决导致崩溃和图形的问题有什么建议吗?如何预防呢?
我在底部包含了一个崩溃的完整示例,以及处理后台任务的代码和处理从成对的 iPhone 发送和接收值的代码。
我从不在没有 replyHandler 的情况下发送消息,所以虽然 others have seen problems because the delegate method for a message without a handler was not implemented 在 iPhone 上,但我认为这不是这里的问题。为了安全起见,我将 iPhone 上的委托方法实现为一个什么都不做的空方法。
2020 年 1 月 30 日更新: 一位朋友建议,问题可能是任务仍在进行中时被 10 秒计时器标记为完成,从而导致内存问题当未决的事情完成时,但不确定可以做些什么。也许这就是这里问题的核心?
这是我来自 ExtensionDelegate
的后台刷新代码:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
// set a timer in case it doesn't complete
// the maximum allowed is 15 seconds, and then it crashes, so schedule the new task and mark complete after 10
var timeoutTimer: Timer? = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { (timer) in
self.scheduleBackgroundRefresh()
backgroundTask.setTaskCompletedWithSnapshot(false)
}
// schedule the next refresh now in case the request crashes
scheduleBackgroundRefresh()
WatchConnectivityManager.shared.requestDataFromPhone()
ComplicationManager.shared.reloadComplication()
// as long as the expiration timer is valid, cancel the timer and set the task complete
// otherwise, we'll assume the timer has fired and the task has been marked complete already
// if it's marked complete again, that's a crash
if let timerValid = timeoutTimer?.isValid, timerValid == true {
timeoutTimer?.invalidate()
timeoutTimer = nil
backgroundTask.setTaskCompletedWithSnapshot(true)
}
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
private func scheduleBackgroundRefresh() {
let fiveMinutesFromNow: Date = Date(timeIntervalSinceNow: 5 * 60)
WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: fiveMinutesFromNow,
userInfo: nil) { (error) in
if let error = error {
fatalError("\(error)")
}
}
}
这里是 WatchConnectivityManager
:
import Foundation
import WatchKit
import WatchConnectivity
class WatchConnectivityManager: NSObject {
static let shared = WatchConnectivityManager()
let session = WCSession.default
private let receivedMessageQueue: OperationQueue = {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
return queue
}()
private func process(messageOrUserInfo: [String : Any]) {
receivedMessageQueue.addOperation {
if let recievedValue = messageOrUserInfo["ValueFromPhone"] as? Int {
DispatchQueue.main.async {
ViewModel.shared.valueFromPhone = recievedValue
}
}
}
}
func requestDataFromPhone() {
if session.activationState == .activated {
let message: [String : Any] = ["Request" : true]
let replyHandler: (([String : Any]) -> Void) = { reply in
self.process(messageOrUserInfo: reply)
}
let errorHandler: ((Error) -> Void) = { error in
}
if session.isReachable {
session.sendMessage(message,
replyHandler: replyHandler,
errorHandler: errorHandler)
}
// send a request to the iPhone as a UserInfo in case the message fails
session.transferUserInfo(message)
}
}
}
extension WatchConnectivityManager: WCSessionDelegate {
func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?) {
if activationState == .activated {
requestDataFromPhone()
}
}
func session(_ session: WCSession,
didReceiveUserInfo userInfo: [String : Any])
{
process(messageOrUserInfo: userInfo)
}
}
崩溃示例:
Hardware Model: Watch3,4
AppVariant: 1:Watch3,4:6
Code Type: ARM (Native)
Role: Non UI
Parent Process: launchd [1]
OS Version: Watch OS 6.1.1 (17S449)
Release Type: User
Baseband Version: n/a
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000004
VM Region Info: 0x4 is not in any region. Bytes before following region: 638972
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
UNUSED SPACE AT START
--->
__TEXT 0009c000-000ac000 [ 64K] r-x/r-x SM=COW ...x/App Name
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [377]
Triggered by Thread: 9
Thread 0 name:
Thread 0:
0 libsystem_kernel.dylib 0x4381f864 semaphore_wait_trap + 8
1 libdispatch.dylib 0x436dac26 _dispatch_sema4_wait + 12 (lock.c:139)
2 libdispatch.dylib 0x436db09a _dispatch_semaphore_wait_slow + 104 (semaphore.c:132)
3 FrontBoardServices 0x46de503e -[FBSSceneSnapshotRequestHandle performRequestForScene:] + 408 (FBSSceneSnapshotRequestHandle.m:67)
4 FrontBoardServices 0x46de96ac -[FBSSceneSnapshotAction snapshotRequest:performWithContext:] + 218 (FBSSceneSnapshotAction.m:168)
5 FrontBoardServices 0x46da4320 -[FBSSceneSnapshotRequest performSnapshotWithContext:] + 292 (FBSSceneSnapshotRequest.m:65)
6 UIKitCore 0x5ba6a000 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_3 + 168 (UIApplication.m:7655)
7 FrontBoardServices 0x46de9568 -[FBSSceneSnapshotAction _executeNextRequest] + 244 (FBSSceneSnapshotAction.m:135)
8 FrontBoardServices 0x46de91e0 -[FBSSceneSnapshotAction executeRequestsWithHandler:completionHandler:expirationHandler:] + 244 (FBSSceneSnapshotAction.m:87)
9 UIKitCore 0x5ba69f20 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_2 + 238 (UIApplication.m:7650)
10 UIKitCore 0x5ba69696 -[UIApplication _beginSnapshotSessionForScene:withSnapshotBlock:] + 772 (UIApplication.m:7582)
11 UIKitCore 0x5ba69e16 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke + 112 (UIApplication.m:7648)
12 UIKitCore 0x5b2c1110 -[UIScene _enableOverrideSettingsForActions:] + 40 (UIScene.m:1206)
13 UIKitCore 0x5b2c1330 -[UIScene _performSystemSnapshotWithActions:] + 112 (UIScene.m:1230)
14 UIKitCore 0x5ba69b90 -[UIApplication _performSnapshotsWithAction:forScene:completion:] + 382 (UIApplication.m:7647)
15 UIKitCore 0x5be89586 __98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionCont... + 146 (_UISceneSnapshotBSActionsHandler.m:54)
16 UIKitCore 0x5ba68fd4 _runAfterCACommitDeferredBlocks + 274 (UIApplication.m:3038)
17 UIKitCore 0x5ba5b3da _cleanUpAfterCAFlushAndRunDeferredBlocks + 198 (UIApplication.m:3016)
18 UIKitCore 0x5ba82702 _afterCACommitHandler + 56 (UIApplication.m:3068)
19 CoreFoundation 0x43b63644 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18 (CFRunLoop.c:1758)
20 CoreFoundation 0x43b5f43c __CFRunLoopDoObservers + 350 (CFRunLoop.c:1868)
21 CoreFoundation 0x43b5f956 __CFRunLoopRun + 1150 (CFRunLoop.c:2910)
22 CoreFoundation 0x43b5f23a CFRunLoopRunSpecific + 370 (CFRunLoop.c:3192)
23 GraphicsServices 0x46973cd0 GSEventRunModal + 96 (GSEvent.c:2246)
24 UIKitCore 0x5ba61580 UIApplicationMain + 1730 (UIApplication.m:4773)
25 libxpc.dylib 0x438fbcf0 _xpc_objc_main.cold.3 + 152
26 libxpc.dylib 0x438eca34 _xpc_objc_main + 184 (main.m:126)
27 libxpc.dylib 0x438ee934 xpc_main + 110 (init.c:1568)
28 Foundation 0x443f3156 -[NSXPCListener resume] + 172 (NSXPCListener.m:276)
29 PlugInKit 0x4b58b26c -[PKService run] + 384 (PKService.m:165)
30 WatchKit 0x52e9dafe WKExtensionMain + 62 (main.m:19)
31 libdyld.dylib 0x43715e82 start + 2
Thread 1 name:
Thread 1:
0 libsystem_kernel.dylib 0x4381f814 mach_msg_trap + 20
1 libsystem_kernel.dylib 0x4381eece mach_msg + 42 (mach_msg.c:103)
2 CoreFoundation 0x43b63946 __CFRunLoopServiceMachPort + 152 (CFRunLoop.c:2575)
3 CoreFoundation 0x43b5f9de __CFRunLoopRun + 1286 (CFRunLoop.c:2931)
4 CoreFoundation 0x43b5f23a CFRunLoopRunSpecific + 370 (CFRunLoop.c:3192)
5 Foundation 0x443bf398 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 180 (NSRunLoop.m:374)
6 Foundation 0x443bf2b4 -[NSRunLoop(NSRunLoop) runUntilDate:] + 76 (NSRunLoop.m:421)
7 UIKitCore 0x5badf012 -[UIEventFetcher threadMain] + 140 (UIEventFetcher.m:637)
8 Foundation 0x444c1b60 __NSThread__start__ + 708 (NSThread.m:724)
9 libsystem_pthread.dylib 0x438ad1ac _pthread_start + 130 (pthread.c:896)
10 libsystem_pthread.dylib 0x438b3f28 thread_start + 20
Thread 2 name:
Thread 2:
0 libsystem_kernel.dylib 0x43836d04 __psynch_cvwait + 24
1 libsystem_pthread.dylib 0x438b02c2 _pthread_cond_wait + 496 (pthread_cond.c:591)
2 libsystem_pthread.dylib 0x438aca4a pthread_cond_wait + 38 (pthread_cancelable.c:558)
3 Foundation 0x444381f0 -[NSOperation waitUntilFinished] + 446 (NSOperation.m:737)
4 Foundation 0x444a5302 __NSOPERATIONQUEUE_IS_WAITING_ON_AN_OPERATION__ + 22 (NSOperation.m:2610)
5 Foundation 0x444222ee -[NSOperationQueue addOperations:waitUntilFinished:] + 128 (NSOperation.m:2618)
6 WatchConnectivity 0x53f9871e __47-[WCSession handleUserInfoResultWithPairingID:]_block_invoke.491 + 540 (WCSession.m:1440)
7 WatchConnectivity 0x53fa5608 -[WCFileStorage enumerateUserInfoResultsWithBlock:] + 1564 (WCFileStorage.m:505)
8 WatchConnectivity 0x53f984ca __47-[WCSession handleUserInfoResultWithPairingID:]_block_invoke_2 + 284 (WCSession.m:1430)
9 Foundation 0x444a4794 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 10 (NSOperation.m:1541)
10 Foundation 0x443d2f7a -[NSBlockOperation main] + 74 (NSOperation.m:1560)
11 Foundation 0x444a63e2 __NSOPERATION_IS_INVOKING_MAIN__ + 22 (NSOperation.m:2184)
12 Foundation 0x443d2b96 -[NSOperation start] + 578 (NSOperation.m:2201)
13 Foundation 0x444a6b7c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 22 (NSOperation.m:2215)
14 Foundation 0x444a6798 __NSOQSchedule_f + 134 (NSOperation.m:2226)
15 libdispatch.dylib 0x436d9846 _dispatch_call_block_and_release + 10 (init.c:1408)
16 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
17 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
18 libdispatch.dylib 0x436dc08c _dispatch_async_redirect_invoke + 520 (queue.c:803)
19 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
20 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
21 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
22 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 3:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 4:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 5:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 6 name:
Thread 6:
0 libsystem_kernel.dylib 0x43838c6c kevent_qos + 24
1 libdispatch.dylib 0x436f2b74 _dispatch_kq_poll + 204 (event_kevent.c:736)
2 libdispatch.dylib 0x436f280a _dispatch_kq_drain + 96 (event_kevent.c:809)
3 libdispatch.dylib 0x436f2196 _dispatch_event_loop_poke + 162 (event_kevent.c:1918)
4 libdispatch.dylib 0x436e32b8 _dispatch_mgr_queue_push + 110 (queue.c:5857)
5 WatchConnectivity 0x53f9aa26 -[WCSession createAndStartTimerOnWorkQueueWithHandler:] + 150 (WCSession.m:1803)
6 WatchConnectivity 0x53f90790 -[WCSession onqueue_sendMessageData:replyHandler:errorHandler:dictionaryMessage:] + 382 (WCSession.m:674)
7 WatchConnectivity 0x53f901da __51-[WCSession sendMessage:replyHandler:errorHandler:]_block_invoke.256 + 190 (WCSession.m:630)
8 Foundation 0x444a4794 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 10 (NSOperation.m:1541)
9 Foundation 0x443d2f7a -[NSBlockOperation main] + 74 (NSOperation.m:1560)
10 Foundation 0x444a63e2 __NSOPERATION_IS_INVOKING_MAIN__ + 22 (NSOperation.m:2184)
11 Foundation 0x443d2b96 -[NSOperation start] + 578 (NSOperation.m:2201)
12 Foundation 0x444a6b7c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 22 (NSOperation.m:2215)
13 Foundation 0x444a6798 __NSOQSchedule_f + 134 (NSOperation.m:2226)
14 libdispatch.dylib 0x436e4c02 _dispatch_block_async_invoke2 + 80 (queue.c:525)
15 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
16 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
17 libdispatch.dylib 0x436dc08c _dispatch_async_redirect_invoke + 520 (queue.c:803)
18 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
19 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
20 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
21 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 7:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 8:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 9 name:
Thread 9 Crashed:
0 libdispatch.dylib 0x436eacec dispatch_channel_cancel + 6 (source.c:1020)
1 WatchConnectivity 0x53f909c6 __81-[WCSession onqueue_sendMessageData:replyHandler:errorHandler:dictionaryMessage:]_block_invoke + 42 (WCSession.m:675)
2 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
3 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
4 libdispatch.dylib 0x436ea81e _dispatch_source_invoke + 1758 (source.c:568)
5 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
6 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
7 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
8 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 10:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 11:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 9 crashed with ARM Thread State (32-bit):
r0: 0x00000000 r1: 0x80000000 r2: 0x00000001 r3: 0x7fffffff
r4: 0x16548f40 r5: 0x00000000 r6: 0x00000110 r7: 0x3f057e58
r8: 0x00000000 r9: 0x00000000 r10: 0x00000000 r11: 0x00401600
ip: 0x66e39628 sp: 0x3f057e30 lr: 0x53f909c7 pc: 0x436eacec
cpsr: 0x80000030
我认为你的朋友是对的:
当您将后台任务设置为完成时,系统将暂停您的应用,请参阅 here。
并且系统可以在没有警告的情况下清除暂停的应用程序,请参阅 here。
因此,如果发生这种情况,将无法调用 replyHandler
或 errorHandler
,并且应用程序会崩溃。
因此,您不能依赖 sendMessage
来唤醒 iOS 应用程序并及时调用 replyHandler
。
我建议您在 iOS 上初始化并发症数据的传输。要定期这样做,您可以使用 silent push notifications that wake up your iOS app, and send new complication data using transferCurrentComplicationUserInfo(_:)
see here。即使您的手表应用不是运行,也会收到此userInfo
,并且可以根据需要更新并发症数据。
编辑:
虽然我上面的解决方案可能有效,但可能有更简单的解决方案:
也许不需要更新您的 ViewModel
来响应 sendMessage
,只需要更新并发症。如果是这样,您可以简单地使用 sendMessage
并将 replyHandler
和 errorHandler
设置为 nil
.
sendMessage
保证在将来唤醒 iOS 应用程序,即使 watchOS 应用程序已经处于非活动状态。 iOS 应用程序随后可以发送并发症数据,这些数据将立即显示。
此外,iOS 应用程序可以发送 userInfo
更新您的 ViewModel
作为应用程序上下文。当 watchOS 应用程序再次激活时,它将可用,并且您可以更新 ViewModel
.
如果这适合您的用例,您可以简单地删除计时器并在 sendMessage
之后立即完成后台任务。
我正在构建一个 watchOS 应用程序,它需要定期从配套 iPhone 应用程序请求信息以刷新复杂功能。
为此,我有一个定期运行的 WKApplicationRefreshBackgroundTask
。它使用 WatchConnectivity
中的 sendMessage(_:replyHandler:errorHandler:)
从 iPhone 应用请求信息,处理回复,并更新并发症。
大多数情况下,这对许多用户来说都是可靠的,但是,我看到 Apple Watch 在与 sendMessage(_:replyHandler:errorHandler:)
相关的 Xcode 中崩溃,我担心这会导致用户错过并发症更新.一些用户一直在抱怨复杂功能没有更新,所以我试图确定是否存在超出 watchOS 中复杂功能刷新频率的常规限制的问题。
有人对我如何解决这个问题有什么建议吗?或者,你对我如何更好地解决导致崩溃和图形的问题有什么建议吗?如何预防呢?
我在底部包含了一个崩溃的完整示例,以及处理后台任务的代码和处理从成对的 iPhone 发送和接收值的代码。
我从不在没有 replyHandler 的情况下发送消息,所以虽然 others have seen problems because the delegate method for a message without a handler was not implemented 在 iPhone 上,但我认为这不是这里的问题。为了安全起见,我将 iPhone 上的委托方法实现为一个什么都不做的空方法。
2020 年 1 月 30 日更新: 一位朋友建议,问题可能是任务仍在进行中时被 10 秒计时器标记为完成,从而导致内存问题当未决的事情完成时,但不确定可以做些什么。也许这就是这里问题的核心?
这是我来自 ExtensionDelegate
的后台刷新代码:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
// set a timer in case it doesn't complete
// the maximum allowed is 15 seconds, and then it crashes, so schedule the new task and mark complete after 10
var timeoutTimer: Timer? = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { (timer) in
self.scheduleBackgroundRefresh()
backgroundTask.setTaskCompletedWithSnapshot(false)
}
// schedule the next refresh now in case the request crashes
scheduleBackgroundRefresh()
WatchConnectivityManager.shared.requestDataFromPhone()
ComplicationManager.shared.reloadComplication()
// as long as the expiration timer is valid, cancel the timer and set the task complete
// otherwise, we'll assume the timer has fired and the task has been marked complete already
// if it's marked complete again, that's a crash
if let timerValid = timeoutTimer?.isValid, timerValid == true {
timeoutTimer?.invalidate()
timeoutTimer = nil
backgroundTask.setTaskCompletedWithSnapshot(true)
}
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
private func scheduleBackgroundRefresh() {
let fiveMinutesFromNow: Date = Date(timeIntervalSinceNow: 5 * 60)
WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: fiveMinutesFromNow,
userInfo: nil) { (error) in
if let error = error {
fatalError("\(error)")
}
}
}
这里是 WatchConnectivityManager
:
import Foundation
import WatchKit
import WatchConnectivity
class WatchConnectivityManager: NSObject {
static let shared = WatchConnectivityManager()
let session = WCSession.default
private let receivedMessageQueue: OperationQueue = {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
return queue
}()
private func process(messageOrUserInfo: [String : Any]) {
receivedMessageQueue.addOperation {
if let recievedValue = messageOrUserInfo["ValueFromPhone"] as? Int {
DispatchQueue.main.async {
ViewModel.shared.valueFromPhone = recievedValue
}
}
}
}
func requestDataFromPhone() {
if session.activationState == .activated {
let message: [String : Any] = ["Request" : true]
let replyHandler: (([String : Any]) -> Void) = { reply in
self.process(messageOrUserInfo: reply)
}
let errorHandler: ((Error) -> Void) = { error in
}
if session.isReachable {
session.sendMessage(message,
replyHandler: replyHandler,
errorHandler: errorHandler)
}
// send a request to the iPhone as a UserInfo in case the message fails
session.transferUserInfo(message)
}
}
}
extension WatchConnectivityManager: WCSessionDelegate {
func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?) {
if activationState == .activated {
requestDataFromPhone()
}
}
func session(_ session: WCSession,
didReceiveUserInfo userInfo: [String : Any])
{
process(messageOrUserInfo: userInfo)
}
}
崩溃示例:
Hardware Model: Watch3,4
AppVariant: 1:Watch3,4:6
Code Type: ARM (Native)
Role: Non UI
Parent Process: launchd [1]
OS Version: Watch OS 6.1.1 (17S449)
Release Type: User
Baseband Version: n/a
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000004
VM Region Info: 0x4 is not in any region. Bytes before following region: 638972
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
UNUSED SPACE AT START
--->
__TEXT 0009c000-000ac000 [ 64K] r-x/r-x SM=COW ...x/App Name
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [377]
Triggered by Thread: 9
Thread 0 name:
Thread 0:
0 libsystem_kernel.dylib 0x4381f864 semaphore_wait_trap + 8
1 libdispatch.dylib 0x436dac26 _dispatch_sema4_wait + 12 (lock.c:139)
2 libdispatch.dylib 0x436db09a _dispatch_semaphore_wait_slow + 104 (semaphore.c:132)
3 FrontBoardServices 0x46de503e -[FBSSceneSnapshotRequestHandle performRequestForScene:] + 408 (FBSSceneSnapshotRequestHandle.m:67)
4 FrontBoardServices 0x46de96ac -[FBSSceneSnapshotAction snapshotRequest:performWithContext:] + 218 (FBSSceneSnapshotAction.m:168)
5 FrontBoardServices 0x46da4320 -[FBSSceneSnapshotRequest performSnapshotWithContext:] + 292 (FBSSceneSnapshotRequest.m:65)
6 UIKitCore 0x5ba6a000 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_3 + 168 (UIApplication.m:7655)
7 FrontBoardServices 0x46de9568 -[FBSSceneSnapshotAction _executeNextRequest] + 244 (FBSSceneSnapshotAction.m:135)
8 FrontBoardServices 0x46de91e0 -[FBSSceneSnapshotAction executeRequestsWithHandler:completionHandler:expirationHandler:] + 244 (FBSSceneSnapshotAction.m:87)
9 UIKitCore 0x5ba69f20 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke_2 + 238 (UIApplication.m:7650)
10 UIKitCore 0x5ba69696 -[UIApplication _beginSnapshotSessionForScene:withSnapshotBlock:] + 772 (UIApplication.m:7582)
11 UIKitCore 0x5ba69e16 __65-[UIApplication _performSnapshotsWithAction:forScene:completion:]_block_invoke + 112 (UIApplication.m:7648)
12 UIKitCore 0x5b2c1110 -[UIScene _enableOverrideSettingsForActions:] + 40 (UIScene.m:1206)
13 UIKitCore 0x5b2c1330 -[UIScene _performSystemSnapshotWithActions:] + 112 (UIScene.m:1230)
14 UIKitCore 0x5ba69b90 -[UIApplication _performSnapshotsWithAction:forScene:completion:] + 382 (UIApplication.m:7647)
15 UIKitCore 0x5be89586 __98-[_UISceneSnapshotBSActionsHandler _respondToActions:forFBSScene:inUIScene:fromTransitionCont... + 146 (_UISceneSnapshotBSActionsHandler.m:54)
16 UIKitCore 0x5ba68fd4 _runAfterCACommitDeferredBlocks + 274 (UIApplication.m:3038)
17 UIKitCore 0x5ba5b3da _cleanUpAfterCAFlushAndRunDeferredBlocks + 198 (UIApplication.m:3016)
18 UIKitCore 0x5ba82702 _afterCACommitHandler + 56 (UIApplication.m:3068)
19 CoreFoundation 0x43b63644 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18 (CFRunLoop.c:1758)
20 CoreFoundation 0x43b5f43c __CFRunLoopDoObservers + 350 (CFRunLoop.c:1868)
21 CoreFoundation 0x43b5f956 __CFRunLoopRun + 1150 (CFRunLoop.c:2910)
22 CoreFoundation 0x43b5f23a CFRunLoopRunSpecific + 370 (CFRunLoop.c:3192)
23 GraphicsServices 0x46973cd0 GSEventRunModal + 96 (GSEvent.c:2246)
24 UIKitCore 0x5ba61580 UIApplicationMain + 1730 (UIApplication.m:4773)
25 libxpc.dylib 0x438fbcf0 _xpc_objc_main.cold.3 + 152
26 libxpc.dylib 0x438eca34 _xpc_objc_main + 184 (main.m:126)
27 libxpc.dylib 0x438ee934 xpc_main + 110 (init.c:1568)
28 Foundation 0x443f3156 -[NSXPCListener resume] + 172 (NSXPCListener.m:276)
29 PlugInKit 0x4b58b26c -[PKService run] + 384 (PKService.m:165)
30 WatchKit 0x52e9dafe WKExtensionMain + 62 (main.m:19)
31 libdyld.dylib 0x43715e82 start + 2
Thread 1 name:
Thread 1:
0 libsystem_kernel.dylib 0x4381f814 mach_msg_trap + 20
1 libsystem_kernel.dylib 0x4381eece mach_msg + 42 (mach_msg.c:103)
2 CoreFoundation 0x43b63946 __CFRunLoopServiceMachPort + 152 (CFRunLoop.c:2575)
3 CoreFoundation 0x43b5f9de __CFRunLoopRun + 1286 (CFRunLoop.c:2931)
4 CoreFoundation 0x43b5f23a CFRunLoopRunSpecific + 370 (CFRunLoop.c:3192)
5 Foundation 0x443bf398 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 180 (NSRunLoop.m:374)
6 Foundation 0x443bf2b4 -[NSRunLoop(NSRunLoop) runUntilDate:] + 76 (NSRunLoop.m:421)
7 UIKitCore 0x5badf012 -[UIEventFetcher threadMain] + 140 (UIEventFetcher.m:637)
8 Foundation 0x444c1b60 __NSThread__start__ + 708 (NSThread.m:724)
9 libsystem_pthread.dylib 0x438ad1ac _pthread_start + 130 (pthread.c:896)
10 libsystem_pthread.dylib 0x438b3f28 thread_start + 20
Thread 2 name:
Thread 2:
0 libsystem_kernel.dylib 0x43836d04 __psynch_cvwait + 24
1 libsystem_pthread.dylib 0x438b02c2 _pthread_cond_wait + 496 (pthread_cond.c:591)
2 libsystem_pthread.dylib 0x438aca4a pthread_cond_wait + 38 (pthread_cancelable.c:558)
3 Foundation 0x444381f0 -[NSOperation waitUntilFinished] + 446 (NSOperation.m:737)
4 Foundation 0x444a5302 __NSOPERATIONQUEUE_IS_WAITING_ON_AN_OPERATION__ + 22 (NSOperation.m:2610)
5 Foundation 0x444222ee -[NSOperationQueue addOperations:waitUntilFinished:] + 128 (NSOperation.m:2618)
6 WatchConnectivity 0x53f9871e __47-[WCSession handleUserInfoResultWithPairingID:]_block_invoke.491 + 540 (WCSession.m:1440)
7 WatchConnectivity 0x53fa5608 -[WCFileStorage enumerateUserInfoResultsWithBlock:] + 1564 (WCFileStorage.m:505)
8 WatchConnectivity 0x53f984ca __47-[WCSession handleUserInfoResultWithPairingID:]_block_invoke_2 + 284 (WCSession.m:1430)
9 Foundation 0x444a4794 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 10 (NSOperation.m:1541)
10 Foundation 0x443d2f7a -[NSBlockOperation main] + 74 (NSOperation.m:1560)
11 Foundation 0x444a63e2 __NSOPERATION_IS_INVOKING_MAIN__ + 22 (NSOperation.m:2184)
12 Foundation 0x443d2b96 -[NSOperation start] + 578 (NSOperation.m:2201)
13 Foundation 0x444a6b7c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 22 (NSOperation.m:2215)
14 Foundation 0x444a6798 __NSOQSchedule_f + 134 (NSOperation.m:2226)
15 libdispatch.dylib 0x436d9846 _dispatch_call_block_and_release + 10 (init.c:1408)
16 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
17 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
18 libdispatch.dylib 0x436dc08c _dispatch_async_redirect_invoke + 520 (queue.c:803)
19 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
20 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
21 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
22 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 3:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 4:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 5:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 6 name:
Thread 6:
0 libsystem_kernel.dylib 0x43838c6c kevent_qos + 24
1 libdispatch.dylib 0x436f2b74 _dispatch_kq_poll + 204 (event_kevent.c:736)
2 libdispatch.dylib 0x436f280a _dispatch_kq_drain + 96 (event_kevent.c:809)
3 libdispatch.dylib 0x436f2196 _dispatch_event_loop_poke + 162 (event_kevent.c:1918)
4 libdispatch.dylib 0x436e32b8 _dispatch_mgr_queue_push + 110 (queue.c:5857)
5 WatchConnectivity 0x53f9aa26 -[WCSession createAndStartTimerOnWorkQueueWithHandler:] + 150 (WCSession.m:1803)
6 WatchConnectivity 0x53f90790 -[WCSession onqueue_sendMessageData:replyHandler:errorHandler:dictionaryMessage:] + 382 (WCSession.m:674)
7 WatchConnectivity 0x53f901da __51-[WCSession sendMessage:replyHandler:errorHandler:]_block_invoke.256 + 190 (WCSession.m:630)
8 Foundation 0x444a4794 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 10 (NSOperation.m:1541)
9 Foundation 0x443d2f7a -[NSBlockOperation main] + 74 (NSOperation.m:1560)
10 Foundation 0x444a63e2 __NSOPERATION_IS_INVOKING_MAIN__ + 22 (NSOperation.m:2184)
11 Foundation 0x443d2b96 -[NSOperation start] + 578 (NSOperation.m:2201)
12 Foundation 0x444a6b7c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 22 (NSOperation.m:2215)
13 Foundation 0x444a6798 __NSOQSchedule_f + 134 (NSOperation.m:2226)
14 libdispatch.dylib 0x436e4c02 _dispatch_block_async_invoke2 + 80 (queue.c:525)
15 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
16 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
17 libdispatch.dylib 0x436dc08c _dispatch_async_redirect_invoke + 520 (queue.c:803)
18 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
19 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
20 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
21 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 7:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 8:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 9 name:
Thread 9 Crashed:
0 libdispatch.dylib 0x436eacec dispatch_channel_cancel + 6 (source.c:1020)
1 WatchConnectivity 0x53f909c6 __81-[WCSession onqueue_sendMessageData:replyHandler:errorHandler:dictionaryMessage:]_block_invoke + 42 (WCSession.m:675)
2 libdispatch.dylib 0x436da8b8 _dispatch_client_callout + 6 (object.m:495)
3 libdispatch.dylib 0x436dc6f8 _dispatch_continuation_pop + 330 (inline_internal.h:2484)
4 libdispatch.dylib 0x436ea81e _dispatch_source_invoke + 1758 (source.c:568)
5 libdispatch.dylib 0x436e6eac _dispatch_root_queue_drain + 540 (inline_internal.h:2525)
6 libdispatch.dylib 0x436e73e0 _dispatch_worker_thread2 + 98 (queue.c:6628)
7 libsystem_pthread.dylib 0x438aecd2 _pthread_wqthread + 158 (pthread.c:2364)
8 libsystem_pthread.dylib 0x438b3f10 start_wqthread + 20
Thread 10:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 11:
0 libsystem_pthread.dylib 0x438b3efc start_wqthread + 0
Thread 9 crashed with ARM Thread State (32-bit):
r0: 0x00000000 r1: 0x80000000 r2: 0x00000001 r3: 0x7fffffff
r4: 0x16548f40 r5: 0x00000000 r6: 0x00000110 r7: 0x3f057e58
r8: 0x00000000 r9: 0x00000000 r10: 0x00000000 r11: 0x00401600
ip: 0x66e39628 sp: 0x3f057e30 lr: 0x53f909c7 pc: 0x436eacec
cpsr: 0x80000030
我认为你的朋友是对的:
当您将后台任务设置为完成时,系统将暂停您的应用,请参阅 here。
并且系统可以在没有警告的情况下清除暂停的应用程序,请参阅 here。
因此,如果发生这种情况,将无法调用 replyHandler
或 errorHandler
,并且应用程序会崩溃。
因此,您不能依赖 sendMessage
来唤醒 iOS 应用程序并及时调用 replyHandler
。
我建议您在 iOS 上初始化并发症数据的传输。要定期这样做,您可以使用 silent push notifications that wake up your iOS app, and send new complication data using transferCurrentComplicationUserInfo(_:)
see here。即使您的手表应用不是运行,也会收到此userInfo
,并且可以根据需要更新并发症数据。
编辑:
虽然我上面的解决方案可能有效,但可能有更简单的解决方案:
也许不需要更新您的 ViewModel
来响应 sendMessage
,只需要更新并发症。如果是这样,您可以简单地使用 sendMessage
并将 replyHandler
和 errorHandler
设置为 nil
.
sendMessage
保证在将来唤醒 iOS 应用程序,即使 watchOS 应用程序已经处于非活动状态。 iOS 应用程序随后可以发送并发症数据,这些数据将立即显示。
此外,iOS 应用程序可以发送 userInfo
更新您的 ViewModel
作为应用程序上下文。当 watchOS 应用程序再次激活时,它将可用,并且您可以更新 ViewModel
.
如果这适合您的用例,您可以简单地删除计时器并在 sendMessage
之后立即完成后台任务。