Core data CoreData崩溃“;Can';无法获取'的值;批次';在绑定中{};重置NSManagedObjectContext时

Core data CoreData崩溃“;Can';无法获取'的值;批次';在绑定中{};重置NSManagedObjectContext时,core-data,Core Data,我正在使用它来设置核心数据堆栈 我的设置包括两个CoreData堆栈,一个由SQLite数据库支持,第二个仅在内存中 在我的应用程序中,在注销期间,我将重置两个堆栈-请参阅代码: 重置内存中核心数据堆栈时,我的应用程序有时会在重置主NSManagedObjectContext的线路上崩溃。我无法在本地复制它 崩溃消息本身是谷歌找不到任何结果的: *** Terminating app due to uncaught exception 'NSInvalidArgumentException',

我正在使用它来设置核心数据堆栈

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

在我的应用程序中,在注销期间,我将重置两个堆栈-请参阅代码:

重置内存中核心数据堆栈时,我的应用程序有时会在重置主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
的任何UI都完全没有内存,这很难确定,但堆栈跟踪提供了一个线索。这里是重置上下文的位置:

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
的任何UI都完全内存不足 正如中所指出的,它是
NSFetchedResultsController
的一个特定实例,在重置主上下文时收到通知,并且可能尝试执行失败的提取,从而引发异常

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

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

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

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)在注销时都会释放。

可能的根本原因 正如中所指出的,它是
NSFetchedResultsController
的一个特定实例,在重置主上下文时收到通知,并且可能尝试执行失败的提取,从而引发异常

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

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

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

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)在注销时都会释放。

谢谢,Tom。我已经从堆栈跟踪中找到了特定的NSFetchedResultController,现在可以重现崩溃。有趣的是,当我从内存存储切换到sqlite时,一切都按预期工作,没有崩溃。这可能是一种竞争条件,取决于每个上下文重置所需的时间。这两者的内部结构一定非常不同。谢谢你,汤姆。我已经从堆栈跟踪中找到了特定的NSFetchedResultController,现在可以重现崩溃。有趣的是,当我从内存存储切换到sqlite时,一切都按预期工作,没有崩溃。这可能是一种竞争条件,取决于每个上下文重置所需的时间。两者的内部结构必须非常不同。