gcd / dispatch_async和CoreData冻结应用程序

时间:2012-12-31 03:56:56

标签: ios core-data grand-central-dispatch freeze dispatch-async

出于某种原因,使用dispatch_async和Core Data会导致我的应用程序完全冻结,但不会崩溃。

症状

用户界面没有响应。该应用程序不会崩溃。

该应用使用[[UIAccelermeter sharedAccelerometer] setDelegate: self]连续获取加速度计值。 “accelerometer:didAccelerate:”回调在主线程上运行。发生此问题时,不再执行回调。

因此主线程似乎是“冻结”。

上下文

我在我的应用中使用NSURLConnection将HTTP请求发送到我的服务器。在connectionDidFinishLoading中,处理响应数据时,执行大约需要2秒,用户界面在这2秒内没有响应,因为它在主线程中运行。

经过一些研究,似乎gcd/dispatch_async是在这种情况下在后台执行数据处理的最佳解决方案。所以,在connectionDidFinishLoading中,我添加了以下gcd调用:

dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    [self handleTheData: connection];
});

这有时会很好,但它会导致应用程序随机冻结 - 即使使用相同的代码,核心数据存储中的数据相同,重复测试表明可以在X%的时间内观察到症状。发生问题的可能性受到一些因素的影响,下面将进一步解释。

handleTheData()做什么?

它检查NSURLConnection的响应数据,这是JSON格式的字符串。该字符串是3个实体列表,每个实体是一个数组数组。因此,有一些代码可以将JSON格式的字符串转换为自定义对象的3个NSMutableArray,其中每个对象都是一个子类NSManagedObject。

在转换期间,由于这些对象是“临时的”,因为它们可能会或可能不会永久存储,所以它们是在“nil”NSManagedObjectContext中创建的,如下所示:

SampleEntity *newEntity= 
(SampleEntity *) [[NSManagedObject alloc] initWithEntity:entity 
                       insertIntoManagedObjectContext:nil
               ];

这样,它们就不会与主要背景混合在一起。

然后将这些新对象与数据存储中的现有实体进行比较。因此,有一些代码重复从数据存储中获取以查看是否已存在特定的新实体,并且根据新实体的数据,新实体可以保存到数据存储,或者可以更新现有实体。 / p>

经常调用[managedObjectContext save:& error]以确保永久保存更改。

还有其他在后台运行吗?

如症状部分所述,使用[[UIAccelermeter sharedAccelerometer] setDelegate:self]

在主线程的后台运行Accelerometer

AudioUnits也已设置为在后台运行(根据Apple文档在单独的“优先级”线程上)以产生声音。

发现问题的结果

发现发生问题的可能性受以下因素影响:

  • 如果某些调试代码被注释掉,例如NSLog(@“fetchedResult:%@”,fetchedResult),则发生问题的可能性会降低(但仍会发生)。
  • 如果不使用加速度计,问题几乎不会发生(但不是100%肯定,因为我进行了有限数量的测试)。
  • 如果使用加速度计,发生问题的几率约为50/50。
  • 如果某些核心数据代码(例如[sampleEntity setXYZ ..])被注释掉,即使使用加速度计,问题也几乎不会发生。

第二期

此外,即使主线程未冻结,也会发生另一个问题。数据处理代码将中途结束而不会抛出错误。

  • 这意味着,数据处理只会部分完成,并且不会抛出任何错误,并且主线程继续正常运行。
  • 每当主要问题没有发生时,第二个问题经常发生(但并非总是如此)。

有趣的问题不是吗? :)

修改

  • 3/1/2013 - 我之前认为问题的原因是iOS 6.抱歉,这是完全错误的。修改后的描述以反映新发现。

1 个答案:

答案 0 :(得分:2)

我最终提供了Apple开发支持的技术支持事件。我也为他们提供了一个样本项目,它显示了相同的症状。

几天之后,我得到了他们的回复,它向我指出了CoreData编程指南,并指出了由调度队列运行的代码需要拥有自己的私有NSManagedObjectContext。

https://developer.apple.com/documentation/coredata/using_core_data_in_the_background

一旦我遵循了建议,问题似乎已经解决了!