GCD dispatch_after 调用导致 SIGBUS 信号

Posted

技术标签:

【中文标题】GCD dispatch_after 调用导致 SIGBUS 信号【英文标题】:GCD dispatch_after call causing SIGBUS signal 【发布时间】:2012-06-25 01:06:53 【问题描述】:

当我的应用程序被加载以执行某些行为时,我正在使用 GCD 的 dispatch_after 方法。预期的行为是在applicationDidFinishLaunchingWithOptions 结束后等待 3 秒,以执行在后台队列中运行的选择器。

我的测试设备没有遇到任何崩溃,但我收到了关于未捕获 SIGBUS 信号的用户崩溃报告,原因是 BUS_ADRALN 异常。根据我对这段代码的理解,BUS_ADRALN 错误表示地址对齐错误。

这就是我创建计时器的方式:

double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
                     ^(void)
                         [self methodToPerformInBackground];
                     );

什么可能导致这个崩溃?

由于多线程错误可能是一种奇怪的野兽,所以我将抛出一些我一直在脑海中折腾的想法。

我在对[self performSelectorOnMainThread:withObject:waitUntilDone] 的调用中调用它。在以这种方式调用的选择器中执行此操作有什么问题吗? 由于我调用的是dispatch_get_global_queue 而不是dispatch_create_queue,所以我不需要保留此方法返回的队列。这个推理正确吗? 在此代码中,self 是应用程序委托。应用程序进入后台或终止后尝试执行块是否会导致崩溃?应用程序是否会在关闭时自动清理任何分派的块? 被调用的方法内部的某些东西导致了崩溃,但 GCD 不提供对其的堆栈跟踪。

编辑:我宁愿不包含在块中调用的代码,因为我不相信这是主要问题。这是堆栈跟踪。线程 0 上的崩溃看起来好像问题出在 GCD 中,而不是块中调用的代码。

编辑#2:在查看了更多崩溃报告后,我有一个奇怪的消息要分享。只有运行 ios 4.2.X 及更低版本的用户才会出现此崩溃。由于 iOS 4.0 及更高版本支持 GCD,我猜测 4.3 中修补了一个错误。

Thread 0 Crashed:
0  libSystem.B.dylib                  0x35e5fb10 _dispatch_retain + 0
1  libSystem.B.dylib                  0x35e5df8c dispatch_after_f + 80
2  libSystem.B.dylib                  0x35e5e070 dispatch_after + 72
3  MyApplication                              0x0000466c -[MyApplicationDelegate applicationDidFinishLaunchingPart2:] (MyApplicationDelegate.m:366)
4  CoreFoundation                      0x37538f79 -[NSObject(NSObject) performSelector:withObject:] + 25
5  Foundation                          0x35171e6d __NSThreadPerformPerform + 273
6  CoreFoundation                      0x375518d1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
7  CoreFoundation                      0x37521ecd __CFRunLoopDoSources0 + 385
8  CoreFoundation                      0x375216f9 __CFRunLoopRun + 265
9  CoreFoundation                      0x3752150b CFRunLoopRunSpecific + 227
10  CoreFoundation                      0x37521419 CFRunLoopRunInMode + 61
11  GraphicsServices                    0x33e76d24 GSEventRunModal + 196
12  UIKit                              0x3591d57c -[UIApplication _run] + 588
13  UIKit                              0x3591a558 UIApplicationMain + 972
14  MyApplication                              0x00003024 main (main.m:113)

Thread 1:
0  libSystem.B.dylib                  0x35d8f974 kevent + 24
1  libSystem.B.dylib                  0x35e5dd70 _dispatch_queue_invoke + 104
2  libSystem.B.dylib                  0x35e5d790 _dispatch_worker_thread2 + 128
3  libSystem.B.dylib                  0x35de6978 _pthread_wqthread + 400

Thread 2:
0  libSystem.B.dylib                  0x35de72fc __workq_kernreturn + 8

Thread 3:
0  libSystem.B.dylib                  0x35d5b3b0 mach_msg_trap + 20
1  CoreFoundation                      0x37521f83 __CFRunLoopServiceMachPort + 95
2  CoreFoundation                      0x37521787 __CFRunLoopRun + 407
3  CoreFoundation                      0x3752150b CFRunLoopRunSpecific + 227
4  CoreFoundation                      0x37521419 CFRunLoopRunInMode + 61
5  WebCore                            0x3318bd1c _ZL12RunWebThreadPv + 532
6  libSystem.B.dylib                  0x35de5b4c _pthread_start + 372

Thread 4:
0  libSystem.B.dylib                  0x35d5b3b0 mach_msg_trap + 20
1  CoreFoundation                      0x37521f83 __CFRunLoopServiceMachPort + 95
2  CoreFoundation                      0x37521787 __CFRunLoopRun + 407
3  CoreFoundation                      0x3752150b CFRunLoopRunSpecific + 227
4  CoreFoundation                      0x37521419 CFRunLoopRunInMode + 61
5  Foundation                          0x3517ec55 +[NSURLConnection(NSURLConnectionReallyInternal) _resourceLoadLoop:] + 217
6  Foundation                          0x3515cb91 -[NSThread main] + 49
7  Foundation                          0x35155b97 __NSThread__main__ + 915
8  libSystem.B.dylib                  0x35de5b4c _pthread_start + 372

【问题讨论】:

如果您声称这是发生崩溃的地方,则需要查看方法内的代码。否则我们该怎么办? @borrrden,该方法中有太多代码需要分享。该方法正在执行一些文件 IO 并读取原子(但不是线程安全的)属性值。我添加了堆栈跟踪。如果这没有帮助,我会添加一些代码。 你静态分析过你的代码吗?在 3 秒调度之前,您是否在屏幕上展示了任何内容? 【参考方案1】:

DISPATCH_QUEUE_PRIORITY_BACKGROUND 是 iOS 5.0+ 的一项功能。如果您尝试在 iOS 4.x 上使用它,它将为 NULL(当您尝试保留它时会崩溃,因为 GCD 是一个 C 库并且使用 NULL 的方式是不安全的 @ 使用方式987654323@ 在目标-c)。请参阅this answer 了解更多信息。解决方案是改用低优先级,或者使用预处理器指令在两者之间切换。

【讨论】:

感谢@borrrden。你完全正确。我不敢相信我在文档中错过了这一点。刚刚看到它下面的“iOS 4.0 及更高版本”。 DISPATCH_QUEUE_PRIORITY_BACKGROUND 在 iOS 4.3 设备上运行良好,即使文档说只有 5.0+ 支持它。 我敢打赌,他们私下在 iOS 4.3 中引入了它,然后在经过测试后在 5.0 中公开。苹果经常做这种事情。 您可能想澄清为什么它在保留时会崩溃。通常,发送nil retain 消息是安全的。在这种情况下不是,因为编译器使用 C 函数 dispatch_retain 代替。 (我认为,只要说“当您尝试使用 dispatch_retain 保留它时”就可以澄清这一点。)

以上是关于GCD dispatch_after 调用导致 SIGBUS 信号的主要内容,如果未能解决你的问题,请参考以下文章

iOS GCD使用dispatch_after、dispatch_time、dispatch_walltime

GCD 延时执行

iOS - dispatch_after解说

iOS开发多线程篇 08 —GCD的常见用法

GCD中如何延迟处理任务

iOS 多线程:『GCD』详尽总结