NSManagedObjectContext -save:导致SIGSEGV崩溃

时间:2017-07-26 21:00:18

标签: ios objective-c core-data nsmanagedobjectcontext

我看到我的应用具有相同的核心数据堆栈跟踪的几个崩溃报告:

enter image description here

我无法重现此次崩溃,但我认为它与级联删除NSManagedObject关系有关。这是通过在堆栈跟踪中看到_propagatePendingDeletesAtEndOfEvent:的调用来判断的。我的NSManagedObjectContext设置如下:

- (NSManagedObjectContext*)managedObjectContext {
    if (_managedObjectContext == nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    }
    return _managedObjectContext;
}

在我的应用程序中,有什么可能导致-save:调用此堆栈跟踪崩溃的想法?

1 个答案:

答案 0 :(得分:1)

从堆栈跟踪中我只能看到它是竞争条件,更具体地说是strong属性或变量上的数据竞争,可能在字典中。由于崩溃发生在主线程上,我的第一个猜测是你在后台线程上使用了一个不应该在后台线程上访问的API。

  • 为什么我认为这是一场数据竞赛?
  • 由于竞争条件,核心数据代码在主线程上崩溃的最可能原因是什么?
  • 修复此错误的一些建议。

为什么我认为这是数据竞争?

因为崩溃发生在objc_retain。这只是经验。在我看到保留对象的崩溃的10个案例中,有9个是因为数据争用。在另一个案例中,罪魁祸首是手动内存管理错误。如果您对更多细节感兴趣,可以查看objc_storeStrong()的来源。

其次,Concurrency section in the Core Data Reference有这个有趣的信息:

  

NSMainQueueConcurrencyType专门用于您的应用程序界面,只能在应用程序的主队列中使用。

     

NSPrivateQueueConcurrencyType配置在初始化时创建自己的队列,并且只能在该队列上使用。因为队列是私有的并且是NSManagedObjectContext实例的内部队列,所以只能通过performBlock:和performBlockAndWait:方法访问它。

由于竞争条件,核心数据代码在主线程上崩溃的最可能原因是什么?

根据您问题中的代码,您使用的是NSMainQueueConcurrencyType,因此不应在具有此“托管上下文”的后台队列中使用核心数据。

我的猜测是你在某个地方从一个背景线程调用核心数据API。

修复此错误的一些建议。

竞争条件并不总是导致崩溃。因此,有时不容易再现这种崩溃。然而,一切都没有丢失。

要查看它是否确实是数据竞争,您必须查看完整的崩溃报告。使用崩溃报告,您不仅可以获得崩溃主线程的回溯,还可以在崩溃发生时获得进程的所有其他线程的回溯。 (只需在崩溃报告中搜索“CoreData”。)如果您非常不走运,任何后台线程都不会显示核心数据API。在这种情况下,你应该看到至少有一个线程有一些“autorelasepoolpop”-frames。如果在背景堆栈跟踪中找到一些“CoreData”框架,请在该堆栈跟踪中找到指向应用程序代码的框架。你有罪魁祸首。

对于调试,只要您调用核心数据API,就可以在所有地方进行一些assert([NSThread isMainThread]);次调用。如果由于断言失败而崩溃,您就会知道问题所在。

如果您使用的是Xcode 9,您可能需要尝试使用新的“主线消毒器”(在“诊断”下的方案设置中,在同样的面板中配置“线程消毒器”。您可能需要“暂停问题”)。如果这没有帮助,也可以试试“Thread Sanitizer”。

您也可以将并发类型切换为NSPrivateQueueConcurrencyType。请确保使用performBlock:performBlockAndWait:调用来包装所有核心数据API调用,否则您将不会遇到更少的崩溃,但崩溃会更多。如果您确实需要使用后台队列中的核心数据,那么这是可行的方法。出于性能原因。

如果你很幸运,这只是一个很小的代码错误,可以在10分钟内修复:-)。如果你非常不走运,应用程序的并发架构就会被打破,你必须(重新)设计它: - /。

希望这有帮助。