CoreData崩溃“无法在绑定{}中获取'批处理'的值。”重置NSManagedObjectContext时

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CoreData崩溃“无法在绑定{}中获取'批处理'的值。”重置NSManagedObjectContext时相关的知识,希望对你有一定的参考价值。

我正在使用JSQCoreDataKit来设置Core Data堆栈。

我的设置包括两个CoreData堆栈,一个由SQLite数据库支持,第二个仅在内存中。

在我的应用程序中,在注销期间,我正在重置两个堆栈 - 请参阅代码:https://github.com/jessesquires/JSQCoreDataKit/blob/develop/Source/CoreDataStack.swift#L142

重置内存核心数据堆栈时,我的应用程序有时会在重置主NSManagedObjectContext的行上崩溃。我无法在本地重现它。

崩溃邮件本身是谷歌没有找到任何结果:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't get value for 'batch' in bindings {
}.'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010dd3312b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010cd6bf41 objc_exception_throw + 48
    2   Foundation                          0x000000010898b3de -[NSComparisonPredicate rightExpression] + 0
    3   Foundation                          0x000000010898c11f -[NSComparisonPredicate evaluateWithObject:substitutionVariables:] + 274
    4   CoreData                            0x000000010d769a08 -[NSDictionaryStoreMap handleFetchRequest:] + 504
    5   CoreData                            0x000000010d768bf3 -[NSMappedObjectStore executeFetchRequest:withContext:] + 243
    6   CoreData                            0x000000010d768a91 -[NSMappedObjectStore executeRequest:withContext:error:] + 193
    7   CoreData                            0x000000010d80c08b __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 1691
    8   CoreData                            0x000000010d8044a6 __55-[NSPersistentStoreCoordinator _routeHeavyweightBlock:]_block_invoke + 86
    9   CoreData                            0x000000010d818519 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 201
    10  libdispatch.dylib                   0x0000000110fcb33d _dispatch_client_callout + 8
    11  libdispatch.dylib                   0x0000000110fd2235 _dispatch_queue_barrier_sync_invoke_and_complete + 392
    12  CoreData                            0x000000010d803e35 _perform + 213
    13  CoreData                            0x000000010d8041bb -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 283
    14  CoreData                            0x000000010d70aac4 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 660
    15  CoreData                            0x000000010d7090e4 -[NSManagedObjectContext executeFetchRequest:error:] + 564
    16  CoreData                            0x000000010d78794a _faultBatchAtIndex + 714
    17  CoreData                            0x000000010d789a4a -[_PFBatchFaultingArray retainedObjectAtIndex:] + 74
    18  CoreData                            0x000000010d789b32 -[_PFBatchFaultingArray objectAtIndex:] + 50
    19  CoreData                            0x000000010d8a2318 __72-[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:]_block_invoke + 200
    20  CoreData                            0x000000010d74c748 developerSubmittedBlockToNSManagedObjectContextPerform + 168
    21  CoreData                            0x000000010d74c61f -[NSManagedObjectContext performBlockAndWait:] + 239
    22  CoreData                            0x000000010d8a1e03 -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] + 691
    23  CoreData                            0x000000010d8a665b __82-[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:]_block_invoke + 1083
    24  CoreData                            0x000000010d74c748 developerSubmittedBlockToNSManagedObjectContextPerform + 168
    25  CoreData                            0x000000010d74c61f -[NSManagedObjectContext performBlockAndWait:] + 239
    26  CoreData                            0x000000010d8a6207 -[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] + 119
    27  CoreFoundation                      0x000000010dcceeac __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    28  CoreFoundation                      0x000000010dccedaa _CFXRegistrationPost + 442
    29  CoreFoundation                      0x000000010dcceaf2 ___CFXNotificationPost_block_invoke + 50
    30  CoreFoundation                      0x000000010dc90792 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1826
    31  CoreFoundation                      0x000000010dc8f90c _CFXNotificationPost + 652
    32  Foundation                          0x00000001089548f2 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
    33  CoreData                            0x000000010d735725 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 773
    34  CoreData                            0x000000010d7cdedf -[NSManagedObjectContext reset] + 1119
    35  JSQCoreDataKit                      0x000000010906b3c8 _T014JSQCoreDataKit04CoreB5StackC5resetySo13DispatchQueueC02onH0_yAA0E6ResultOc10completiontFyycfU_ + 88
    36  JSQCoreDataKit                      0x000000010906b3ec _T014JSQCoreDataKit04CoreB5StackC5resetySo13DispatchQueueC02onH0_yAA0E6ResultOc10completiontFyycfU_TA + 12
    37  JSQCoreDataKit                      0x0000000109065599 _T0Ix_IyB_TR + 41
    38  CoreData                            0x000000010d74c748 developerSubmittedBlockToNSManagedObjectContextPerform + 168
    39  CoreData                            0x000000010d74c61f -[NSManagedObjectContext performBlockAndWait:] + 239
    40  JSQCoreDataKit                      0x000000010906ae2d _T014JSQCoreDataKit04CoreB5StackC5resetySo13DispatchQueueC02onH0_yAA0E6ResultOc10completiontF + 317
    41  MyApp                            0x000000010c4f362a _T08MyApp14DataRepositoryV09resetCoreD5Stackyyyc10completion_tFZyAA19AsyncBlockOperationCcfU0_ + 106
    42  MyApp                            0x000000010c717761 _T08MyApp19AsyncBlockOperationC5startyyF + 433
    43  MyApp                            0x000000010c7177c4 _T08MyApp19AsyncBlockOperationC5startyyFTo + 36
    44  Foundation                          0x0000000108983577 __NSOQSchedule_f + 369
    45  libdispatch.dylib                   0x0000000110fcb33d _dispatch_client_callout + 8
    46  libdispatch.dylib                   0x0000000110fd65f9 _dispatch_main_queue_callback_4CF + 628
    47  CoreFoundation                      0x000000010dcf5e39 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    48  CoreFoundation                      0x000000010dcba462 __CFRunLoopRun + 2402
    49  CoreFoundation                      0x000000010dcb9889 CFRunLoopRunSpecific + 409
    50  GraphicsServices                    0x0000000112dfc9c6 GSEventRunModal + 62
    51  UIKit                               0x000000010a0875d6 UIApplicationMain + 159
    52  GoOut                               0x00000001061e3857 main + 55
    53  libdyld.dylib                       0x0000000111047d81 start + 1
)
答案

很难确定,但堆栈跟踪提供了线索。这是您重置上下文的位置:

34  CoreData                            0x000000010d7cdedf -[NSManagedObjectContext reset] + 1119

更深层次,这种情况发生:

26  CoreData                            0x000000010d8a6207 -[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] + 119

接下来是其他获取的结果控制器的东西,包括像

22  CoreData                            0x000000010d8a1e03 -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] + 691

这表明你正在重置上下文,而NSFetchedResultsController仍然试图让你的UI保持最新。它注意到由重置引起的更改并尝试处理这些更改,但由于重置正在进行,因此会失败。

这可能是为什么reset难以安全使用的一个例子。如果您有任何以任何方式使用或引用上下文的对象,reset可能会造成麻烦。在调用NSFetchedResultsController之前,您可能需要确保使用reset的任何UI都完全没有内存。

另一答案

The possible root cause

正如Tom Harringtonhis answer中指出的那样,它是NSFetchedResultsController的一个特定实例,当主要上下文被重置时可能会被通知并且可能尝试执行失败的获取,从而导致异常被抛出。

有趣的是,只有在使用内存存储时才会发生这种情况。切换到SQLite存储时,问题就消失了。由于出于性能原因我想继续使用内存存储,我想出了以下解决方法。

The workaround

为了防止NSFetchedResultsController崩溃,我让FRC在上下文(和整个CoreData堆栈)重置之前停止观察上下文中的更改。

这是通过从重置CoreData堆栈的方法发布通知并将FRC的委托设置为nil来完成的。以下代码来自ViewController,其中使用了此特定FRC。

private func setupObservers() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(didReceiveDataRepositoryWillResetNotification(_:)),
        name: Notification.Name.DataRepository.WillResetCoreData,
        object: nil
    )
}

@objc private func didReceiveDataRepositoryWillResetNotification(_ notification: Notification) {
    // HAX: Workaround for FRC throwing an exception when linked to a context backed by in-memory store
    // More info: https://stackoverflow.com/q/49052482/1161723
    fetchedResultsController?.delegate = nil
}

请注意,我没有在任何地方再次设置FRC的委托属性,因为无论如何整个UIViewController堆栈(包括FRC)在注销时已取消分配。

以上是关于CoreData崩溃“无法在绑定{}中获取'批处理'的值。”重置NSManagedObjectContext时的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 在插入和删除时崩溃同时发生

使用 CoreData 获取时崩溃

如何在保存时调试 CoreData 崩溃?

保存到 Coredata 崩溃

CoreData 崩溃

不兼容的 CoreData 存储是不是总是导致崩溃?