1

以下代码在 iOS 模拟器中产生崩溃。

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDate *sleepStart = [NSDate date];
        while ([sleepStart timeIntervalSinceNow] > -300) {

        }
    });    
}

更新:即使在后台线程上也会出现此问题

下面的代码也有问题:

- (void)viewDidLoad
{
    [super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSDate *sleepStart = [NSDate date];
        while ([sleepStart timeIntervalSinceNow] > -300) {

        }
    });    
}

就这样。将其粘贴到任何视图控制器中,在模拟器中运行该应用程序正好 4 分 15 秒,它就会崩溃。

崩溃是我以前从未见过的类型。它是“EXC_??? (11)”。奇怪的是,您可以在崩溃后按下继续按钮,它会像往常一样继续。

为什么会崩溃?如何在不导致此行为的情况下将长任务提交到队列?

以下是我迄今为止尝试过的一些事情,但对这个谜团没有任何启示:

  • 创建我自己的调度队列(包括后台线程)
  • 使用 NSBlockOperation 而不是 GCD(仍然崩溃)
  • 插入睡眠。短暂的睡眠(不到 5 秒左右)似乎在睡眠时延迟了崩溃。更长的睡眠似乎有更多的作用。所以如果你睡一次五秒钟,它会在 4m 20s 而不是 4m 15s 时崩溃。如果你睡一次 60 秒,大约需要十分钟的时间来崩溃,但它最终会发生。这个线索似乎很重要,但我不知道它可能意味着什么。

更新#1

该问题仅在 LLDB 下重现,而不在 GDB 下重现。

4

2 回答 2

5

您不能在主线程上执行冗长的操作。您应该将块分派到另一个线程,然后在块分派结束时返回主线程(如果需要)。

使用dispatch_async(dispatch_get_main_queue(),^{});会导致您的操作在不久的将来在主线程上执行(阻塞)。

主线程受定时器保护;如果你停止响应事件,iOS 会杀死它。这是故意的:不要在主线程上做繁重的工作!

iOS 的容忍度通常远少于四分钟,但如果你在调试,它会变得更长。模拟器有自己的规则。

启动时,只需几秒钟。但是你不应该在主线程上做任何事情超过一瞬间,然后才能响应直接的用户操作(例如用户点击某物)。在主线程上工作会导致 iOS 用户界面变得反应迟钝、生涩而不是玻璃般光滑。

如果您的应用程序停止响应 OSX 上的主线程上的事件,它就会成为沙滩球。您的应用程序停止响应 iOS 上主线程上的事件,iOS 看门狗将其取出并射击。

来自技术说明 TN2151

异常代码 0x8badf00d 表示应用程序已被 iOS 终止,因为发生了看门狗超时。应用程序启动、终止或响应系统事件的时间过长。造成这种情况的一个常见原因是在主线程上进行同步网络。线程 0 上的任何操作:都需要移至后台线程,或以不同方式处理,以免阻塞主线程。

一般来说,模式是:

- (IBAction)tappedWhatever:(id)sender {
    // visually start operation
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
        // do work here, off main thread
        // (you can't update the UI here)
        dispatch_async(dispatch_get_main_queue(),^{
            // show progress in UI
        });
        // more heavy lifting
        dispatch_async(dispatch_get_main_queue(),^{
            // update UI to show operation complete and move to next step
        });
    });
};

(我本可以发誓在 Apple 的文档中对此有更好的描述,但我现在找不到。有人吗?)

于 2013-03-04T03:44:35.773 回答
3

来自Apple 工程师

您看到的“崩溃”并不是真正的崩溃,它是应用程序在持续一段时间内使用过多 CPU 时间时的日志。它存在的理由是告诉你不要那样做,以及 CPU 时间用在了哪里。在生产中,日志只是简单地记录下来,您的过程应该继续。但如果你能避免这些类型的日志,那就更好了。

然后:

您应该提交错误报告。lldb 应该足够聪明,可以默认忽略该陷阱。

于 2013-03-05T03:50:37.497 回答