在 performBlock 中重置我的 NSManagedObjectContext 时,iOS 8 应用程序崩溃

Posted

技术标签:

【中文标题】在 performBlock 中重置我的 NSManagedObjectContext 时,iOS 8 应用程序崩溃【英文标题】:iOS 8 App crashing when resetting my NSManagedObjectContext while in a performBlock 【发布时间】:2014-10-07 11:02:53 【问题描述】:

当我的一个 NSManagedObjectContext 运行它的performBlock 时,用户选择退出他们的帐户(这将重置所有上下文并删除持久存储)。这个重置代码显然是在与 performBlock 类似的时间运行的,因此应用程序崩溃并出现异常:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'

它崩溃的确切位置似乎是当一个上下文保存在其performBlock 中时,然后它将发出did save 通知,并合并到另一个上下文中。这是堆栈跟踪:

0   CoreFoundation                      0x03b96df6 __exceptionPreprocess + 182

1   libobjc.A.dylib                     0x03820a97 objc_exception_throw + 44

2   CoreData                            0x034f7791 _PFRetainedObjectIDCore + 1169

3   CoreData                            0x034f72f0 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:] + 32

4   CoreData                            0x034db0f3 -[NSManagedObjectContext objectWithID:] + 595

5   CoreData                            0x0352e14e _faultBatchAtIndex + 1102

6   CoreData                            0x0352ed02 -[_PFBatchFaultingArray objectAtIndex:] + 50

7   CoreData                            0x035dd010 +[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 144

8   CoreData                            0x035d7ba2 -[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 738

9   CoreData                            0x035da3dd __77-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]_block_invoke + 2285

10  CoreData                            0x034e7454 developerSubmittedBlockToNSManagedObjectContextPerform + 196

11  CoreData                            0x034e7337 -[NSManagedObjectContext performBlockAndWait:] + 231

12  CoreData                            0x035d9acf -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 127

13  Foundation                          0x00eff929 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40

14  CoreFoundation                      0x03b61974 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20

15  CoreFoundation                      0x03a4f61b _CFXNotificationPost + 3051

16  Foundation                          0x00eeef26 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98

17  CoreData                            0x034c84d3 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83

18  CoreData                            0x034d9cbe -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 3934

19  CoreData                            0x034d8d40 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 496

20  App                              0x00319707 __57-[ContextManager contextDidSavePrivateQueueContext:]_block_invoke + 103

这仅适用于 ios 8 - iOS 7 工作正常。我无法弄清楚这是否是我感受到影响的 iOS 8 的一个错误,或者 Apple 是否在 Core Data 中引入了“新功能”而破坏了我们的重置流程。任何人都可以对此有所了解吗?

【问题讨论】:

“我的 NSManagedObjectContext 之一”。这似乎表明你有不止一个。他们是亲子还是同龄人? 一个是私有的,一个是主要的。没有父母/子女。 如果你使用队列限制,你应该使用父子,否则你会看到这样的问题。 【参考方案1】:

我通过在NSManagedObjectContext 上添加一个类别并使用objc_setAssociatedObject 魔法添加一个标志来修复该错误。该标志用于确定上下文是否可以安全地执行任何出现的 performBlock。为此,我在名为safePerformBlock 的类别上添加了另一个方法,它查看标志。如果为假,我立即返回,而不是处理传入的块。

当我删除上下文中的持久存储时,该标志设置为不安全。当用户随后重新登录并重新创建持久存储时,该标志将设置为安全。

本质上,我将上下文中的此标志视为 performBlocks 上的取消标记。我不知道为什么 API 中没有任何内容。在这种情况下,这是我能找到的最佳解决方案。

【讨论】:

【参考方案2】:

Core Data 不是线程安全的。 NSManagedObjectContext 只能在创建它的线程中使用。一旦持久存储消失,就不能使用它。 iOS 8 中的计时很可能略有不同,但由于您不能依赖计时,因此很可能它始终是您的代码中的一个错误。

【讨论】:

正如发帖者所指出的,他正在使用带有 performBlock 的队列限制,可以安全地从任何线程调用。【参考方案3】:

您可以将重置操作放在与 performBlock 相同的线程中,并将队列置于“无并发”模式。所以只有 performBlock 完成后才会重置。

【讨论】:

以上是关于在 performBlock 中重置我的 NSManagedObjectContext 时,iOS 8 应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

ManagedObjectContext performBlock(AndWait) 死锁

指向 performBlock 之外的 NSManagedObject 的指针

只能在使用队列创建的 NSManagedObjectContext 上使用 -performBlock:

NSThread gtm_performBlock 错误

在 performBlock 中分配时 NSManagedObjectContext nil

NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心