CoreData 严重应用错误
Posted
技术标签:
【中文标题】CoreData 严重应用错误【英文标题】:CoreData serious application error 【发布时间】:2014-07-28 17:14:25 【问题描述】:每隔一段时间,我都会在我的应用程序中收到以下错误消息。这发生在我的应用程序通过带有 CoreData 集成的 RestKit 接收到 Web 服务响应后:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. no object at index 3 in section at index 0 with userInfo (null)
2014-07-28 12:48:26.348 Identify[43074:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no object at index 3 in section at index 0'
*** First throw call stack:
(
0 CoreFoundation 0x0270e1e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0248d8e5 objc_exception_throw + 44
2 CoreData 0x006d7dbe -[NSFetchedResultsController objectAtIndexPath:] + 398
3 Identify 0x0002d03e -[WTSRecordListViewController configureCell:atIndexPath:] + 190
4 Identify 0x0002c5f6 -[WTSRecordListViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] + 646
5 CoreData 0x006df296 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 5238
6 Foundation 0x02160049 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40
7 CoreFoundation 0x02769f04 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
8 CoreFoundation 0x026c1efb _CFXNotificationPost + 2859
9 Foundation 0x02099e41 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98
10 CoreData 0x005e1a13 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83
11 CoreData 0x00680faf -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 367
12 CoreData 0x005dceb8 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2152
13 CoreData 0x005e02fc -[NSManagedObjectContext save:] + 140
14 Identify 0x000d1ffd __61-[NSManagedObjectContext(RKAdditions) saveToPersistentStore:]_block_invoke + 125
15 CoreData 0x0060584f developerSubmittedBlockToNSManagedObjectContextPerform + 95
16 CoreData 0x0060578c -[NSManagedObjectContext performBlockAndWait:] + 140
17 Identify 0x000d1b83 -[NSManagedObjectContext(RKAdditions) saveToPersistentStore:] + 739
18 Identify 0x00048d28 __80-[WTSObjectManager getRecordsForUser:forGroup:startIndex:limit:success:failure:]_block_invoke_2 + 2456
19 Identify 0x0014f551 __66-[RKObjectRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke244 + 97
20 libdispatch.dylib 0x02f747b8 _dispatch_call_block_and_release + 15
21 libdispatch.dylib 0x02f894d0 _dispatch_client_callout + 14
22 libdispatch.dylib 0x02f77726 _dispatch_main_queue_callback_4CF + 340
23 CoreFoundation 0x0277343e __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
24 CoreFoundation 0x026b45cb __CFRunLoopRun + 1963
25 CoreFoundation 0x026b39d3 CFRunLoopRunSpecific + 467
26 CoreFoundation 0x026b37eb CFRunLoopRunInMode + 123
27 GraphicsServices 0x0367c5ee GSEventRunModal + 192
28 GraphicsServices 0x0367c42b GSEventRun + 104
29 UIKit 0x0114df9b UIApplicationMain + 1225
30 Identify 0x0000279d main + 141
31 libdyld.dylib 0x031be701 start + 1
一旦进入此状态,如果不删除应用程序并将其重新添加到设备上,就无法修复它。我正在使用苹果为核心数据获取结果控制器定义的库存样板代码:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
NSLog(@"begin updates");
[self.tableView beginUpdates];
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
switch(type)
case NSFetchedResultsChangeInsert:
NSLog(@"inserting section");
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"deleting section");
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
UITableView *tableView = self.tableView;
switch(type)
case NSFetchedResultsChangeInsert:
NSLog(@"inserting row");
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"deleting row");
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
NSLog(@"update row");
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
NSLog(@"move row");
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
NSLog(@"end updates");
[self.tableView endUpdates];
configureCell
方法看起来像这样,当我从 fetchedResultsController 获取记录时失败了:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
//if a nil cell was passed into this method, just return
if (!cell)
return;
WTSRecordCell *recordCell = (WTSRecordCell *)cell;
WTSRecord *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
...
以下代码是我创建 fetchedResultsController 的地方。在我的应用程序中,我创建了两个单独的 WTSRecordListViewController
实例,一个具有按用户返回“记录”的提取请求,另一个按组返回“记录”。
- (NSFetchedResultsController *)fetchedResultsController
if (_fetchedResultsController == nil)
WTSUserInfo *userInfo = [WTSConfiguration sharedConfig].orgConfig.userInfo;
NSFetchRequest *fetchRequest = nil;
if (self.type == RecordListTypeUser)
fetchRequest = [[WTSObjectManager sharedManager] fetchRequestForRecordsByUser:userInfo.username];
else if (self.type == RecordListTypeGroup)
fetchRequest = [[WTSObjectManager sharedManager] fetchRequestForRecordsByGroup:userInfo.groupId];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createDt" ascending:NO];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
DDLogWarn(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
return _fetchedResultsController;
我不确定我在这里做错了什么,以及这是否是 Apple 代码中的某种错误。就像我之前所说的,这种情况只会偶尔发生一次(可能是在后端核心数据处于某种非常特定的状态时,因为在它发生后我似乎永远无法重新创建它)。
编辑:
经过一些额外的搜索,我找到了导致问题的代码。在 RestKit 中的 success
块中,我正在进行 Web 服务调用,我遍历了一些设置某些值的数据库对象。当我完成对值的迭代时,我调用[context saveToPersistentStore]
;该调用是导致获取结果控制器方法被触发的原因。如果我注释掉 saveToPersistentStore
调用,我不会得到异常。
RestKit 中的success
块在主线程上执行。此外,根据我的打印输出,该块在 controllerWillChangeContent:
和 controllerDidChangeContent:
方法之外执行。
编辑 2: 以下代码是我通过 RestKit 进行 Web 服务调用的地方。在其中,我遍历映射数组中返回的对象,设置如何获取记录以供以后进行孤儿清除。我还对未从 fetch 请求返回的记录执行 fetch 请求并清除一个标志(也用于孤立清除)。
在我的 RestKit 成功块中,我没有检查以确保映射请求中返回的对象已被删除,或者从 fetch 请求中返回的对象在修改它们之前已被删除。这是我需要做的事情吗?
[self.rkObjectManager getObjectsAtPathForRouteNamed:kWTSRecordsRouteName object:nil parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
NSArray *records = [mappingResult array];
//we put the record ids we get back in the request in a set
NSMutableSet *recordIds = [NSMutableSet new];
//set in the record how this was fetched
NSManagedObjectContext *context = nil;
for (WTSRecord *record in records)
[recordIds addObject:record.recordId];
if (!context)
context = record.managedObjectContext;
if (groupId)
record.fetchedViaGroup = [NSNumber numberWithBool:YES];
else
record.fetchedViaUser = [NSNumber numberWithBool:YES];
//if the startIndex is 0, the user is refreshing. We clear the 'fetchedVia*' field for any records that DID NOT come back in this request
if (startIndex == 0)
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"WTSRecord"];
if (groupId)
request.predicate = [NSPredicate predicateWithFormat:@"recordId != nil AND fetchedViaGroup == 1 AND pendingDelete == 0"];
else
request.predicate = [NSPredicate predicateWithFormat:@"recordId != nil AND fetchedViaUser == 1 AND pendingDelete == 0", user];
NSArray *records = [context executeFetchRequest:request error:nil];
for (WTSRecord *record in records)
if (![recordIds containsObject:record.recordId])
if (groupId)
record.fetchedViaGroup = [NSNumber numberWithBool:NO];
else
record.fetchedViaUser = [NSNumber numberWithBool:NO];
//save the changes to the persistent store
[context saveToPersistentStore:nil];
success(records);
dispatch_semaphore_signal(sem);
failure:^(RKObjectRequestOperation *operation, NSError *error)
failure(error);
dispatch_semaphore_signal(sem);
];
编辑 3 我在上面的 sn-p 中找到了导致问题的代码:
//set in the record how this was fetched
NSManagedObjectContext *context = nil;
for (WTSRecord *record in records)
[recordIds addObject:record.recordId];
if (!context)
context = record.managedObjectContext;
// if (groupId)
// record.fetchedViaGroup = [NSNumber numberWithBool:YES];
// else
// record.fetchedViaUser = [NSNumber numberWithBool:YES];
//
注释掉以下行后,我可以毫无问题地拨打saveToPersistentStore
。我在这里做错了什么?修改在 mappingResult 中返回的对象并将更改保存到本地数据存储的可接受方法是什么?
【问题讨论】:
我在将 FRC 连接到 tableView 时遇到了类似的问题。对我来说,问题是在 FRC 更新我的 tableView 时调用了 tableView 的 reloadData 方法。在我删除了重新加载方法之后。我没有得到错误。所以可能是 dataSource 和 FRC 之间存在冲突。 第一行给出了问题的线索 no object at index 0 in section at index 0 with userInfo (null) 没有看到你的代码我猜你有一个有4个部分的tableview第 4 节有一个场景,您已经删除了单元格并试图在表格知道发生了什么之前插入 查看我上面的编辑。我找到了导致问题的代码,但我仍然不清楚它为什么会导致任何问题。 修改和保存的代码是做什么的?它会更改已删除的对象吗?你有保护/检查吗? Wain,请参阅我帖子的编辑 2。我不是在检查删除,但是在修改从 mappingResult 或从 fetch 请求返回的对象时,您需要这样做吗? 【参考方案1】:在不知道您如何设置视图控制器的所有细节的情况下,我的最佳答案是,当我遇到这个问题时,这个问题通常发生在我身上。
A.将表格视图插入到视图控制器中
B.将数据保存在同一个视图控制器中。
不管我的情况如何,如果您要将数据保存在一个也有表格视图的视图中,您需要删除代码中的以下方法:
- (void) controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
与
-(void) controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
发生的错误是因为获取的结果突然发生了表格视图无法识别的变化,即。 “no object at index 3 in section at index 0
”。
如果您想让表格响应更改,您需要添加一个refresh
方法并在数据保存后调用它(听起来很明显,但 Stack Exchange 有很多人不明白这一点) IE。 [self refresh];
所以在你的 -(IBAction *) save: (id) sender
方法(或者 void 方法,如果你使用它来保存),从那里调用它。
希望这对将来的任何人都有帮助。
【讨论】:
以上是关于CoreData 严重应用错误的主要内容,如果未能解决你的问题,请参考以下文章
CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常
如何解决由于 iCloud 合并而导致的“CoreData:错误:严重的应用程序错误”?
Swift CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。
写入 coredata,即使在后台线程上完成,也会严重阻塞 UI
使用 NSFetchedResultsController 时 CoreData 与 UITableView 有啥关系?