重置存储时核心数据崩溃:“无法从此 NSManagedObjectContext 的协调器访问对象的持久存储”

Core data crash when resetting the store: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'

我在用 Swift 编写的 iOS 应用程序中尝试删除核心数据的持久存储时发生崩溃。流程很简单:当我从应用程序注销时,我删除了商店:

destroyPersistentStoreAtURL

我在应用程序中使用本机核心数据实现,并且每次访问托管对象都是通过 performBlock/performBlockAndWait 进行的。此外,这些操作都在 NSOperationQueue 中。流程如下:

  1. 注销
  2. cancelAllOperations & waitUntilAllOperationsAreFinishedperformBlocks 实现的队列上
  3. maxConcurrentOperationCount = 1performBlocks
  4. 的队列中
  5. 最后,我在之前的 NSOperationQueue
  6. 中添加了一个销毁持久存储的操作

有时,我会崩溃,但我不明白为什么。据我所知,它与 managedObjectsIDs 和 retain 有关。看看:

2016-11-14 15:51:58.053 ******[3912:179074] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
*** First throw call stack:
(
 0   CoreFoundation                      0x000000010ca0f34b __exceptionPreprocess + 171
 1   libobjc.A.dylib                     0x000000010c05321e objc_exception_throw + 48
 2   CoreData                            0x000000010c5683b2 _PFRetainedObjectIDCore + 1074
 3   CoreData                            0x000000010c5507fc -[NSManagedObjectContext objectWithID:] + 668
 4   CoreData                            0x000000010c590264 _faultBatchAtIndex + 1524
 5   CoreData                            0x000000010c59217a -[_PFBatchFaultingArray retainedObjectAtIndex:] + 74
 6   CoreData                            0x000000010c592262 -[_PFBatchFaultingArray objectAtIndex:] + 50
 7   CoreData                            0x000000010c67d9de __72-[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:]_block_invoke + 190
 8   CoreData                            0x000000010c55adc7 developerSubmittedBlockToNSManagedObjectContextPerform + 199
 9   CoreData                            0x000000010c55ac7f -[NSManagedObjectContext performBlockAndWait:] + 255
 10  CoreData                            0x000000010c67d3b6 -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] + 694
 11  CoreData                            0x000000010c681d75 __82-[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:]_block_invoke + 1077
 12  CoreData                            0x000000010c55adc7 developerSubmittedBlockToNSManagedObjectContextPerform + 199
 13  CoreData                            0x000000010c55ac7f -[NSManagedObjectContext performBlockAndWait:] + 255
 14  CoreData                            0x000000010c681927 -[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] + 119
 15  CoreFoundation                      0x000000010c9ad19c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
 16  CoreFoundation                      0x000000010c9ad09b _CFXRegistrationPost + 427
 17  CoreFoundation                      0x000000010c9ace02 ___CFXNotificationPost_block_invoke + 50
 18  CoreFoundation                      0x000000010c96fea2 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 2018
 19  CoreFoundation                      0x000000010c96ef3b _CFXNotificationPost + 667
 20  Foundation                          0x000000010bb1b0ab -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
 21  CoreData                            0x000000010c5432b0 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 704
 22  CoreData                            0x000000010c562b50 -[NSManagedObjectContext(_NSInternalNotificationHandling) _processChangedStoreConfigurationNotification:] + 2976
 23  CoreData                            0x000000010c5d97ed __95-[NSManagedObjectContext(_NSInternalNotificationHandling) _sendOrEnqueueNotification:selector:]_block_invoke + 109
 24  CoreData                            0x000000010c55adc7 developerSubmittedBlockToNSManagedObjectContextPerform + 199
 25  libdispatch.dylib                   0x000000010e0250cd _dispatch_client_callout + 8
 26  libdispatch.dylib                   0x000000010e0058d6 _dispatch_main_queue_callback_4CF + 406
 27  CoreFoundation                      0x000000010c9d34f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
 28  CoreFoundation                      0x000000010c998f8d __CFRunLoopRun + 2205
 29  CoreFoundation                      0x000000010c998494 CFRunLoopRunSpecific + 420
 30  GraphicsServices                    0x000000010f5d0a6f GSEventRunModal + 161
 31  UIKit                               0x000000010a050964 UIApplicationMain + 159
 32  ******                              0x000000010817e932 main + 114
 33  libdyld.dylib                       0x000000010e07168d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

非常感谢任何帮助!

该错误告诉您,在销毁从中获取托管对象的持久存储后,您仍在尝试使用该托管对象。这肯定会导致这次崩溃。

不可能确切地说出这是在哪里发生的,但是如果您在删除存储后保留对托管对象的 any 引用,您就会得到这个。如果最后您仍在尝试使用不再具有持久性存储的托管对象,则取消操作、使用 performBlock 等都没有任何区别。

@汤姆

是的,你是对的。所以我post你举个例子:

let queue = NSOperationQueue

queue.addOperationWithBlock{
  let moc = newPrivateQueueManagedObjectContext()
  moc.performBlock {
    //some work to do on the context
}

然后有时我会销毁商店,但在此之前我会取消对上述队列的操作。不过,有时我会看到崩溃...

您需要重置在此操作之前使用的 managedObjectContext

致电managedObjectContext.reset()

我遇到了同样的问题和 Tom 的提示,假设脏对象(曾经存在于旧存储中)在我从协调器中删除持久存储后仍然是托管对象上下文的一部分。在我的例子中,我实现了一个 "revert to last saved document version",它需要确保首先丢弃这些脏对象。

[_managedObjectContext reset]
[_managedObjectContext.persistentStoreCoordinator removePersistentStore:_store error:outError]

如果您的代码中仍有对这些对象的引用,object.managedObjectContext 将为 nil — 这是一个很好的恢复提示。