在后台moc中删除对象然后在主moc中刷新它导致NSFetchedResultsController更新崩溃
我遇到了一个我无法理解的NSObjectInaccessibleException
:
*** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x8c40040 <x-coredata://85B02C1C-1FFC-4CBF-B7AC-EEA259115427/Event/p6>'' *** First throw call stack: ( 0 CoreFoundation 0x01aa15e4 __exceptionPreprocess + 180 1 libobjc.A.dylib 0x018248b6 objc_exception_throw + 44 2 CoreData 0x0025733b _PFFaultHandlerLookupRow + 2715 3 CoreData 0x00256897 -[NSFaultHandler fulfillFault:withContext:forIndex:] + 39 4 CoreData 0x00256473 _PF_FulfillDeferredFault + 259 5 CoreData 0x002562c6 _sharedIMPL_pvfk_core + 70 6 CoreData 0x0025acd5 -[NSManagedObject(_PFDynamicAccessorsAndPropertySupport) _genericValueForKey:withIndex:flags:] + 85 7 CoreData 0x00294781 _PF_Handler_Public_GetProperty + 161 8 CoreData 0x00294685 -[NSManagedObject valueForKey:] + 149 9 Foundation 0x01471a5a -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 409 10 Foundation 0x015079ba -[NSSortDescriptor compareObject:toObject:] + 166 11 CoreData 0x00366175 +[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 309 12 CoreData 0x00360f61 -[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 737 13 CoreData 0x00362c44 -[NSFetchedResultsController(PrivateMethods) _postprocessUpdatedObjects:] + 916 14 CoreData 0x00363578 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2152 15 Foundation 0x014f7bf9 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40 16 CoreFoundation 0x01afd524 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20 17 CoreFoundation 0x01a5500b _CFXNotificationPost + 2859 18 Foundation 0x01431951 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98 19 CoreData 0x00268173 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83 20 CoreData 0x0030778f -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 367 21 CoreData 0x00263e38 -[NSManagedObjectContext(_NSInternalChangeProcessing) _postRefreshedObjectsNotificationAndClearList] + 136 22 CoreData 0x002639e4 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 3140 23 CoreData 0x00262d99 -[NSManagedObjectContext processPendingChanges] + 41 24 CoreData 0x00236fe1 _performRunLoopAction + 321 25 CoreFoundation 0x01a694ce __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30 26 CoreFoundation 0x01a6941f __CFRunLoopDoObservers + 399 27 CoreFoundation 0x01a47344 __CFRunLoopRun + 1076 28 CoreFoundation 0x01a46ac3 CFRunLoopRunSpecific + 467 29 CoreFoundation 0x01a468db CFRunLoopRunInMode + 123 30 GraphicsServices 0x039029e2 GSEventRunModal + 192 31 GraphicsServices 0x03902809 GSEventRun + 104 32 UIKit 0x00592d3b UIApplicationMain + 1225 33 RefreshDeletedTest 0x00005d9d main + 141 34 libdyld.dylib 0x020df70d start + 1 ) libc++abi.dylib: terminating with uncaught exception of type _NSCoreDataException
它是由一个特定的代码stream引起的:
- (void)deleteObject:(NSManagedObject *)object { NSManagedObjectID *objID = object.objectID; NSManagedObjectContext *dataProcessingMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; dataProcessingMoc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator; dataProcessingMoc.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataProcessingContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:dataProcessingMoc]; // If I use performBlockAndWait: instead of performBlock: the issue goes away. [dataProcessingMoc performBlock:^{ NSManagedObject *objInDPMoc = [dataProcessingMoc objectWithID:objID]; NSLog(@"Delete: %@", objInDPMoc); [dataProcessingMoc deleteObject:objInDPMoc]; // 1 [object.managedObjectContext performBlockAndWait:^{ [object.managedObjectContext refreshObject:object mergeChanges:NO]; // 2 }]; [dataProcessingMoc save:NULL]; // 3 }]; } - (void)dataProcessingContextDidSave:(NSNotification *)note { NSManagedObjectContext *moc = self.managedObjectContext; [moc performBlockAndWait:^{ // 3 [moc mergeChangesFromContextDidSaveNotification:note]; NSError *error; if ([moc hasChanges] && ![moc save:&error]) { NSLog(@"Unresolved error %@", error); abort(); } }]; }
注意:代码必须看起来很奇怪,没有什么意义。 这是我将真实代码减less到最小错误代码的结果。
- 删除背景moc中的一个对象。
- 刷新同一个对象,将其转化为主moc中的错误。
- 保存背景moc并使用
NSManagedObjectContextDidSaveNotification
将其更改合并到主moc中。
这个exception发生在真正的合并之前,在主要moc的performBlockAndWait:
调用中performBlockAndWait:
。
这是在崩溃时的背景moc的线程堆栈:
#0 0x02210fb6 in semaphore_wait_trap () #1 0x01e3ccde in _dispatch_thread_semaphore_wait () #2 0x01e3a516 in _dispatch_barrier_sync_f_slow () #3 0x01e3a413 in dispatch_barrier_sync_f () #4 0x0028bf1f in -[NSManagedObjectContext performBlockAndWait:] () #5 0x000037b5 in -[MasterViewController dataProcessingContextDidSave:] at /Users/an0/dev/Projects/iOS/Study/RefreshDeletedTest/RefreshDeletedTest/MasterViewController.m:84 #6 0x014f7bf9 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke () #7 0x01afd524 in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ () #8 0x01a5500b in _CFXNotificationPost () #9 0x01431951 in -[NSNotificationCenter postNotificationName:object:userInfo:] () #10 0x0027bb2c in -[NSManagedObjectContext(_NSInternalAdditions) _didSaveChanges] () #11 0x0026720f in -[NSManagedObjectContext save:] () #12 0x0000345a in __37-[MasterViewController cleanObjects:]_block_invoke at /Users/an0/dev/Projects/iOS/Study/RefreshDeletedTest/RefreshDeletedTest/MasterViewController.m:78 #13 0x003047c3 in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync () #14 0x01e4d4b0 in _dispatch_client_callout () #15 0x01e3b07f in _dispatch_queue_drain () #16 0x01e3ae7a in _dispatch_queue_invoke () #17 0x01e3be1f in _dispatch_root_queue_drain () #18 0x01e3c137 in _dispatch_worker_thread2 () #19 0x021d9dab in _pthread_wqthread ()
为什么这样崩溃? 是我的代码真的错了,或者是Core Data
的错误?
另一个我不明白的地方是,在这样的崩溃之后,应用程序不能再被启动,因为在启动时会崩溃:
2014-01-05 12:31:34.151 RefreshDeletedTest[3546:70b] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0xa88d060 <x-coredata://93009ECF-EC6E-4B12-BC40-997C4BF3B8DF/Event/p11>'' *** First throw call stack: ( 0 CoreFoundation 0x01aa05e4 __exceptionPreprocess + 180 1 libobjc.A.dylib 0x018238b6 objc_exception_throw + 44 2 CoreData 0x0025633b _PFFaultHandlerLookupRow + 2715 3 CoreData 0x00255897 -[NSFaultHandler fulfillFault:withContext:forIndex:] + 39 4 CoreData 0x00255473 _PF_FulfillDeferredFault + 259 5 CoreData 0x002552c6 _sharedIMPL_pvfk_core + 70 6 CoreData 0x00259cd5 -[NSManagedObject(_PFDynamicAccessorsAndPropertySupport) _genericValueForKey:withIndex:flags:] + 85 7 CoreData 0x00293781 _PF_Handler_Public_GetProperty + 161 8 CoreData 0x00293685 -[NSManagedObject valueForKey:] + 149 9 Foundation 0x014a8f0a -[NSArray(NSKeyValueCoding) valueForKey:] + 456 10 Foundation 0x01470a5a -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 409 11 Foundation 0x0149b99d -[NSArray(NSKeyValueCoding) valueForKeyPath:] + 580 12 CoreData 0x0035b7e9 -[NSFetchedResultsController performFetch:] + 697 13 RefreshDeletedTest 0x00003910 -[MasterViewController fetchedResultsController] + 800 14 RefreshDeletedTest 0x0000307e -[MasterViewController numberOfSectionsInTableView:] + 78 15 UIKit 0x007e5b94 -[UITableViewRowData(UITableViewRowDataPrivate) _updateNumSections] + 102 16 UIKit 0x007e6993 -[UITableViewRowData invalidateAllSections] + 69 17 UIKit 0x0065d237 -[UITableView _updateRowData] + 194 18 UIKit 0x0065d170 -[UITableView _ensureRowDataIsLoaded] + 45 19 UIKit 0x006704b3 -[UITableView numberOfSections] + 35 20 UIKit 0x0084d3d3 -[UITableViewController viewWillAppear:] + 103 21 UIKit 0x006a8bfa -[UIViewController _setViewAppearState:isAnimating:] + 419 22 UIKit 0x006a9108 -[UIViewController __viewWillAppear:] + 114 23 UIKit 0x006cb3f5 -[UINavigationController _startTransition:fromViewController:toViewController:] + 800 24 UIKit 0x006cc09c -[UINavigationController _startDeferredTransitionIfNeeded:] + 645 25 UIKit 0x006cccb9 -[UINavigationController __viewWillLayoutSubviews] + 57 26 UIKit 0x00806181 -[UILayoutContainerView layoutSubviews] + 213 27 UIKit 0x0e35656f -[UILayoutContainerViewAccessibility(SafeCategory) layoutSubviews] + 50 28 UIKit 0x005fc267 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355 29 libobjc.A.dylib 0x0183581f -[NSObject performSelector:withObject:] + 70 30 QuartzCore 0x045802ea -[CALayer layoutSublayers] + 148 31 QuartzCore 0x045740d4 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380 32 QuartzCore 0x04580235 -[CALayer layoutIfNeeded] + 160 33 UIKit 0x006b7613 -[UIViewController window:setupWithInterfaceOrientation:] + 304 34 UIKit 0x005d6177 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 5212 35 UIKit 0x005d4d16 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82 36 UIKit 0x005d4be8 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117 37 UIKit 0x005d4c70 -[UIWindow _setRotatableViewOrientation:duration:force:] + 67 38 UIKit 0x005d3d0a __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120 39 UIKit 0x005d3c6c -[UIWindow _updateToInterfaceOrientation:duration:force:] + 400 40 UIKit 0x005d49c3 -[UIWindow setAutorotates:forceUpdateInterfaceOrientation:] + 870 41 UIKit 0x005d7fb6 -[UIWindow setDelegate:] + 449 42 UIKit 0x006a9737 -[UIViewController _tryBecomeRootViewControllerInWindow:] + 180 43 UIKit 0x005cdc1c -[UIWindow addRootViewControllerViewIfPossible] + 609 44 UIKit 0x005cdd97 -[UIWindow _setHidden:forced:] + 312 45 UIKit 0x005ce02d -[UIWindow _orderFrontWithoutMakingKey] + 49 46 UIKit 0x0e332c66 -[UIWindowAccessibility(SafeCategory) _orderFrontWithoutMakingKey] + 77 47 UIKit 0x005d889a -[UIWindow makeKeyAndVisible] + 65 48 UIKit 0x0058bcd0 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1851 49 UIKit 0x005903a8 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824 50 UIKit 0x005a487c -[UIApplication handleEvent:withNewEvent:] + 3447 51 UIKit 0x005a4de9 -[UIApplication sendEvent:] + 85 52 UIKit 0x00592025 _UIApplicationHandleEvent + 736 53 GraphicsServices 0x039032f6 _PurpleEventCallback + 776 54 GraphicsServices 0x03902e01 PurpleEventCallback + 46 55 CoreFoundation 0x01a1bd65 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53 56 CoreFoundation 0x01a1ba9b __CFRunLoopDoSource1 + 523 57 CoreFoundation 0x01a4677c __CFRunLoopRun + 2156 58 CoreFoundation 0x01a45ac3 CFRunLoopRunSpecific + 467 59 CoreFoundation 0x01a458db CFRunLoopRunInMode + 123 60 UIKit 0x0058fadd -[UIApplication _run] + 840 61 UIKit 0x00591d3b UIApplicationMain + 1225 62 RefreshDeletedTest 0x0000508d main + 141 63 libdyld.dylib 0x020de70d start + 1 ) libc++abi.dylib: terminating with uncaught exception of type _NSCoreDataException
以下是您可以运行以查看问题的示例项目: http : //d.pr/f/y3VQ
在multithreading环境中,无法避免此问题。
只要在同一层次结构级别上有两个不同的上下文(无论是父子架构还是直接连接到存储),您可能会遇到一种情况,即一个上下文删除了一个对象(并将更改提交给商店),另一个尝试在合并更改之前访问此对象。
当父上下文提交改变到商店并且其子试图访问他保存的已删除项作为错误时,也可能发生这种情况。
一般来说,在提交之前上下文之间不会自动传播更改的每个地方,都有机会获得此exception。
如果你可以pipe理你的上下文(store-context1-context2 -…)并且只从链中的最终上下文中删除,你可以避免这个问题。
你可以devise一些algorithm来延迟你的删除操作,所以只有在没有其他上下文使用某个特定的项目的时候才会提交它们,但是这涉及到大量的簿记工作。
您可以通过获取完整的(无故障)对象或您将要处理的整个子图(使用FRC时无法完成)并对这些对象进行操作来减less上下文之间的冲突。