我正在开发一个在后台工作的库。
为了确保从主队列调用UIKit方法,我在NSThread上有这个类别:
+(void) d360_ensureMainThreadSync:(dispatch_block_t) onMainBlock
{
if ([NSThread isMainThread]) {
onMainBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), onMainBlock);
}
}
并像这样使用它:
__block UIUserNotificationSettings *currentSettings;
dispatch_block_t onMainBlock = ^{
currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
};
[NSThread d360_ensureMainThreadSync:onMainBlock];
// do something with currentSettings
我需要dispatch_sync
执行,因为我之后会立即使用结果。
我知道在主线程上调用dispatch_sync(dispatch_get_main_queue()
会导致死锁,因此检查[NSThread isMainThread]
问题是应用程序有时冻结,我发现semaphore_wait_trap
中有一个线程,而其他线程正在等待它完成_dispatch_sync_wait
。
这当然是随机发生的,很难再现。
当情况发生时以及在xcode中暂停调试器时,以下是lldb thread backtrace all
的输出。
您可以看到thread #1
位于semaphore_wait_trap
,而thread #5
和thread #8
正在等待dispatch_sync_wait
来自d360_ensureMainThreadSync
。< / p>
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x0000000183e1cc00 libsystem_kernel.dylib`semaphore_wait_trap + 8
frame #1: 0x00000001023714fc libdispatch.dylib`_dispatch_sema4_wait + 24
frame #2: 0x0000000102372210 libdispatch.dylib`_dispatch_group_wait_slow + 196
frame #3: 0x00000001023760c0 libdispatch.dylib`dispatch_block_wait + 264
frame #4: 0x000000018680a9a4 BaseBoard`-[NSObject(BaseBoard) bs_performSynchronously:timeout:] + 192
frame #5: 0x000000018d9b2754 UIKit`-[UIApplication _userNotificationTypes] + 128
frame #6: 0x000000018d9b2674 UIKit`-[UIApplication currentUserNotificationSettings] + 28
* frame #7: 0x00000001009f8e30 D360Kit`__70-[D360PushNotificationStatusProvider dictionaryWithKeyValueParameters]_block_invoke((null)=0x00000001b4ce8f88) at D360PushNotificationStatusProvider.m:24
frame #8: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #9: 0x000000010237bdac libdispatch.dylib`_dispatch_sync_thread_bound_invoke + 124
frame #10: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #11: 0x000000010236e050 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 1192
frame #12: 0x00000001842cbf20 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
frame #13: 0x00000001842c9afc CoreFoundation`__CFRunLoopRun + 2012
frame #14: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #15: 0x000000018607bf84 GraphicsServices`GSEventRunModal + 100
frame #16: 0x000000018d797880 UIKit`UIApplicationMain + 208
frame #17: 0x000000010057ac18 D360TestApp`main(argc=1, argv=0x000000016f89b9c8) at main.m:11
frame #18: 0x0000000183d0e56c libdyld.dylib`start + 4
thread #5, queue = 'com.d360.EventsNetworkQueue (QOS: UNSPECIFIED)'
frame #0: 0x0000000183e3dc1c libsystem_kernel.dylib`__ulock_wait + 8
frame #1: 0x00000001023716e8 libdispatch.dylib`_dispatch_ulock_wait + 48
frame #2: 0x0000000102371840 libdispatch.dylib`_dispatch_thread_event_wait_slow + 36
frame #3: 0x000000010237baec libdispatch.dylib`_dispatch_sync_wait + 448
frame #4: 0x0000000100a4f9c8 D360Kit`+[NSThread(self=NSThread, _cmd="d360_ensureMainThreadSync:", onMainBlock=0x00000001009f8d6c) d360_ensureMainThreadSync:] at NSThread+D360Thread.m:17
frame #5: 0x00000001009f8aa8 D360Kit`-[D360PushNotificationStatusProvider dictionaryWithKeyValueParameters](self=0x00000001c4013d30, _cmd="dictionaryWithKeyValueParameters") at D360PushNotificationStatusProvider.m:28
frame #6: 0x00000001009f83c8 D360Kit`__75-[NSSet((null)=<unavailable>, provider=0x00000001c4013d30, stop=NO) d360_JSONDictionaryFromParameterProviders]_block_invoke at NSSet+D360ParametersProviders.m:20
frame #7: 0x0000000184206370 CoreFoundation`-[__NSSetM enumerateObjectsWithOptions:usingBlock:] + 224
frame #8: 0x00000001009f8338 D360Kit`-[NSSet(self=8 elements, _cmd="d360_JSONDictionaryFromParameterProviders") d360_JSONDictionaryFromParameterProviders] at NSSet+D360ParametersProviders.m:18
frame #9: 0x00000001009fec24 D360Kit`-[D360DeviceNetworkModel JSONDictionary](self=0x00000001c462c6e0, _cmd="JSONDictionary") at D360DeviceNetworkModel.m:50
frame #10: 0x00000001009ffe90 D360Kit`-[D360EventsNetworkModel JSONDictionary](self=0x00000001c46276e0, _cmd="JSONDictionary") at D360EventsNetworkModel.m:46
frame #11: 0x00000001009d86c8 D360Kit`-[D360Client logEvents:completion:](self=0x00000001c064a2f0, _cmd="logEvents:completion:", eventsModel=0x00000001c46276e0, completion=0x00000001009f305c) at D360Client.m:85
frame #12: 0x00000001009f2c6c D360Kit`-[D360SendEventsOperation start](self=0x00000001c42b68c0, _cmd="start") at D360SendEventsOperation.m:99
frame #13: 0x0000000184cf0004 Foundation`__NSOQSchedule_f + 404
frame #14: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #15: 0x0000000102376800 libdispatch.dylib`_dispatch_continuation_pop + 592
frame #16: 0x000000010237509c libdispatch.dylib`_dispatch_async_redirect_invoke + 628
frame #17: 0x000000010237ab54 libdispatch.dylib`_dispatch_root_queue_drain + 616
frame #18: 0x000000010237a880 libdispatch.dylib`_dispatch_worker_thread3 + 136
frame #19: 0x0000000183f4f130 libsystem_pthread.dylib`_pthread_wqthread + 1268
frame #20: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #7, name = 'com.apple.uikit.eventfetch-thread'
frame #0: 0x0000000183e1cbc4 libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000183e1ca3c libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001842cbce4 CoreFoundation`__CFRunLoopServiceMachPort + 196
frame #3: 0x00000001842c98b0 CoreFoundation`__CFRunLoopRun + 1424
frame #4: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #5: 0x0000000184c126e4 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 304
frame #6: 0x0000000184c31afc Foundation`-[NSRunLoop(NSRunLoop) runUntilDate:] + 96
frame #7: 0x000000018e2e302c UIKit`-[UIEventFetcher threadMain] + 136
frame #8: 0x0000000184d13860 Foundation`__NSThread__start__ + 996
frame #9: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #10: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #11: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #8, queue = 'com.apple.usernotifications.UNUserNotificationServiceConnection.call-out'
frame #0: 0x0000000183e3dc1c libsystem_kernel.dylib`__ulock_wait + 8
frame #1: 0x00000001023716e8 libdispatch.dylib`_dispatch_ulock_wait + 48
frame #2: 0x0000000102371840 libdispatch.dylib`_dispatch_thread_event_wait_slow + 36
frame #3: 0x000000010237baec libdispatch.dylib`_dispatch_sync_wait + 448
frame #4: 0x0000000100a4f9c8 D360Kit`+[NSThread(self=NSThread, _cmd="d360_ensureMainThreadSync:", onMainBlock=0x0000000100a1a1a0) d360_ensureMainThreadSync:] at NSThread+D360Thread.m:17
frame #5: 0x0000000100a1a108 D360Kit`+[D360ApplicationContext currentContext](self=D360ApplicationContext, _cmd="currentContext") at D360ApplicationContext.m:55
frame #6: 0x0000000100a02a58 D360Kit`-[D360RemoteNotificationController notificationInboxController:didFindActionModel:](self=0x00000001c4259500, _cmd="notificationInboxController:didFindActionModel:", controller=0x00000001c0233960, actionModel=0x00000001c04a1c80) at D360RemoteNotificationController.m:278
frame #7: 0x0000000100a4f378 D360Kit`-[D360NotificationInboxController processNotification:](self=0x00000001c0233960, _cmd="processNotification:", notification=0x00000001c043aee0) at D360NotificationInboxController.m:98
frame #8: 0x0000000100a4ec68 D360Kit`__63-[D360NotificationInboxController processDeliveredNotification]_block_invoke((null)=<unavailable>, notifications=@"16 elements") at D360NotificationInboxController.m:50
frame #9: 0x000000010236949c libdispatch.dylib`_dispatch_call_block_and_release + 24
frame #10: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #11: 0x0000000102378110 libdispatch.dylib`_dispatch_queue_serial_drain + 692
frame #12: 0x000000010236c9a4 libdispatch.dylib`_dispatch_queue_invoke + 332
frame #13: 0x0000000102379104 libdispatch.dylib`_dispatch_root_queue_drain_deferred_wlh + 424
frame #14: 0x0000000102380100 libdispatch.dylib`_dispatch_workloop_worker_thread + 652
frame #15: 0x0000000183f4efe0 libsystem_pthread.dylib`_pthread_wqthread + 932
frame #16: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #13, name = 'com.apple.CFSocket.private'
frame #0: 0x0000000183e3d570 libsystem_kernel.dylib`__select + 8
frame #1: 0x00000001842d421c CoreFoundation`__CFSocketManager + 644
frame #2: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #3: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #4: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #16, name = 'com.apple.NSURLConnectionLoader'
frame #0: 0x0000000183e1cbc4 libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000183e1ca3c libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001842cbce4 CoreFoundation`__CFRunLoopServiceMachPort + 196
frame #3: 0x00000001842c98b0 CoreFoundation`__CFRunLoopRun + 1424
frame #4: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #5: 0x0000000184953b40 CFNetwork`+[NSURLConnection(Loader) _resourceLoadLoop:] + 404
frame #6: 0x0000000184d13860 Foundation`__NSThread__start__ + 996
frame #7: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #8: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #9: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #21
frame #0: 0x0000000183e3ddbc libsystem_kernel.dylib`__workq_kernreturn + 8
frame #1: 0x0000000183f4efb0 libsystem_pthread.dylib`_pthread_wqthread + 884
frame #2: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #22
frame #0: 0x0000000183f4ec2c libsystem_pthread.dylib`start_wqthread
@ Cy-4AH向我指出这个似乎是Apple's internal framework的bs_performSynchronously
我现在设法使用
重现冻结@interface NSObject()
- (BOOL)bs_performSynchronously:(id /* block */)arg1;
@end
...
[NSThread d360_ensureMainThreadSync:^{
[self bs_performSynchronously:^{
NSLog(@"Test");
}];
}];
现在问题是这个bs_performSynchronously
是什么以及如何避免它无限期地等待。
我尝试在ensureMainThreadSync
使用dispatch_async
而不是dispatch_sync
时更改我的实现,并使用信号量等待它,如下所示:
+ (void)d360_ensureMainThreadSync:(dispatch_block_t)onMainBlock
{
if ([NSThread isMainThread]) {
onMainBlock();
} else {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_sync(dispatch_get_main_queue(), ^{
onMainBlock();
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
}
但它没有帮助。
我认为可能确实是因为@CRD提及使用了已弃用的API currentUserNotificationSettings
。
答案 0 :(得分:0)
在iOS 10+和[[UIApplication sharedApplication] currentUserNotificationSettings]
中运行时,有问题的代码似乎已弃用dispatch_sync(dispatch_get_main_queue()
。
在iOS 10+中使用UserNotifications
是有效的。在我的例子中,我需要以同步的方式获取这些值,因此我使用dispatch_semaphore_t
来等待完成块。要与旧版iOS兼容,代码为:
// iOS 10+ use the UNUserNotificationCenter
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion) { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }]) {
__block UNNotificationSettings *currentSettings;
// create a semaphore to wait for the asynchronous block
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) {
currentSettings = settings;
dispatch_semaphore_signal(sema);
}];
// the calling code will wait here until getNotificationSettingsWithCompletionHandler completes
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// handle notification settings
// ...
} else {
__block UIUserNotificationSettings *currentSettings;;
// UKit methods need to be run on a main thread. Implementation of ensureMainThreadSync is above
[NSThread d360_ensureMainThreadSync:^{
currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
}];
// handle notification settings
// ...
}
所以,即使我不确定基本问题是什么,这个解决方案对我有用,到目前为止我没有看到任何死锁。