我在iOS 8.1上遇到CoreData并发问题。崩溃:NSInternalInconsistencyException - 无效的rowCache行是零
我得到以下堆栈跟踪崩溃:
NSInternalInconsistencyException - Invalid rowCache row is nil
0 CoreFoundation 0x0000000183b6659c __exceptionPreprocess + 132
1 libobjc.A.dylib 0x00000001942640e4 objc_exception_throw + 56
2 CoreData 0x000000018385b8b8 -[NSSQLCore _newRowCacheRowForToManyUpdatesForRelationship:rowCacheOriginal:originalSnapshot:value:added:deleted:sourceRowPK:properties:sourceObject:newIndexes:reorderedIndexes:] + 6668
3 CoreData 0x00000001838fbea0 -[NSSQLCore recordToManyChangesForObject:inRow:usingTimestamp:inserted:] + 2604
4 CoreData 0x0000000183857638 -[NSSQLCore prepareForSave:] + 1052
5 CoreData 0x00000001838569b4 -[NSSQLCore saveChanges:] + 520
6 CoreData 0x000000018381f078 -[NSSQLCore executeRequest:withContext:error:] + 716
7 CoreData 0x00000001838e6254 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 4048
8 CoreData 0x00000001838ed654 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 176
9 libdispatch.dylib 0x00000001948a936c _dispatch_client_callout + 12
10 libdispatch.dylib 0x00000001948b26e8 _dispatch_barrier_sync_f_invoke + 72
11 CoreData 0x00000001838e0cb4 _perform + 176
12 CoreData 0x000000018381ec34 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 296
13 CoreData 0x0000000183845400 -[NSManagedObjectContext save:] + 1280
这崩溃发生在几个地方(约20),但在我的数据导入功能,其中最突出的进口记录1000和保存有关我的CoreData设置每100
的几个注意事项:
- 我使用"Stack #3" from this blog post
- 我所有的管理对象更新/保存代码在一个
performBlock:
- 所做的崩溃都在后台背景
,所有这些都是节约的代码发生具有以下基本模式:
[backgroundContext performBlock:^{
...
NSArray *result = [backgroundContext fetch...];
...
if ([backgroundContext save:&error]) { // <-- App is crashing here
}
}];
据我了解,不应该有与backgroundContext
而T3背后的NSPersistentStore
任何并发问题帽子是坠机告诉我的。
此外,这只发生在我的用户群的0.02%以内。这是非常罕见的(我无法重现),但用户是而不是能够从此恢复而不删除并重新安装应用程序。它会一直打开并为他们崩溃。
请注意,这仅适用于64位iOS 8.1.X - iOS 7.X和8.0.X不会显示此行为,并且在任何比iPhone 5s更旧的东西上我都看不到它。
说明:删除持久性存储可以为所有用户解决此问题。这似乎表明它不是并发问题。
创建商店&上下文。
[_persistenStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[DatabaseManager storeURL]
options:@{NSMigratePersistentStoresAutomaticallyOption:@YES,
NSInferMappingModelAutomaticallyOption:@YES}
error:&error];
创建上下文
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[_backgroundContext setPersistentStoreCoordinator:_persistenStoreCoordinator];
if ([_backgroundContext respondsToSelector:@selector(setName:)]) {
[_backgroundContext setName:@"DatabaseManager.BackgroundQueue"];
}
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[_mainContext setPersistentStoreCoordinator:_persistenStoreCoordinator];
if ([_mainContext respondsToSelector:@selector(setName:)]) {
[_mainContext setName:@"DatabaseManager.MainQueue"];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mainContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:_mainContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:_backgroundContext];
听力变化
- (void) mainContextDidSave:(NSNotification *)notification
{
[_backgroundContext performBlock:^{
[self->_backgroundContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
- (void) backgroundContextDidSave:(NSNotification*)notification
{
[_mainContext performBlock:^{
NSArray* updated = [notification.userInfo valueForKey:NSUpdatedObjectsKey];
// Fault all objects that will be updated.
for (NSManagedObject* obj in updated) {
NSManagedObject* mainThreadObject = [self->_mainContext objectWithID:obj.objectID];
[mainThreadObject willAccessValueForKey:nil];
}
[self->_mainContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
如何代码在每个上下文中执行。我传递一个块到这里:
- (void)executeBackgroundOperation:(void (^)(NSManagedObjectContext *))operation {
NSAssert(operation, @"No Background operation to perform");
[_backgroundContext performBlock:^{
operation(self->_backgroundContext);
}];
}
- (void)executeMainThreadOperation:(void (^)(NSManagedObjectContext *))operation {
NSAssert(operation, @"No Main Thread operation to perform");
[_mainContext performBlock:^{
operation(self->_mainContext);
}];
}
的崩溃报告的唯一其他线程,在他们有一个lock
是这个样子两个JavaScript线程:
0 libsystem_kernel.dylib 0x3a77cb38 __psynch_cvwait + 24
1 libsystem_pthread.dylib 0x3a7fa2dd pthread_cond_wait + 38
2 libc++.1.dylib 0x39a11e91 _ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE + 34
3 JavaScriptCore 0x2dcd4cb5 _ZN3JSC8GCThread16waitForNextPhaseEv + 102
4 JavaScriptCore 0x2dcd4d19 _ZN3JSC8GCThread12gcThreadMainEv + 50
5 JavaScriptCore 0x2db09597 _ZN3WTFL19wtfThreadEntryPointEPv + 12
6 libsystem_pthread.dylib 0x3a7f9e93 _pthread_body + 136
7 libsystem_pthread.dylib 0x3a7f9e07 _pthread_start + 116
8 libsystem_pthread.dylib 0x3a7f7b90 thread_start + 6
嘿斯蒂芬,当多个上下文指向单个nsmanagedobject并在同一时间修改关系并尝试保存上下文时,会发生此崩溃。确保在修改表格及其数据时调用保存。 – Punita 2015-02-18 12:02:10
@Punita我只有一个上下文保存到持久存储区,所有'save:'调用都在'performBlock:'上进行,所以除非上下文并行执行块,否则我不会看到这会发生。 – 2015-02-18 14:54:02
正如你所写**更新/保存代码是在performBlock **和**上完成的**崩溃全部在后台上下文**上。所以我的理解是,你在后台上下文中插入数据,而不是在主线程的performBlock中保存托管对象。如果我是正确的,那么在执行保存时,它会通知NSManagedObjectContextDidSaveNotification背景的上下文,并且在此通知中,您将** info字典包含具有被插入,删除和更新的托管对象的数组**如Apple文档中所述:与核心数据并发 – Punita 2015-02-19 07:51:20