首先,抱歉这个问题太长了。
我知道这里讨论类似问题的问题很少,但这些问题都没有谈到NSFetchedResultsController与委托以及单独线程中的更新。并没有一个解决方案帮助了我 这些是现有的问题:
现在关于我的问题:
NSFetchedResultsController
实例,并且委托设置为self。有时我会在更新单独线程中的数据时收到was mutated while being enumerated
异常,有时会导致应用崩溃。
我已经做了很多代码操作,试图修复它,似乎没有任何帮助
我已经尝试不直接从表视图数据源方法使用托管对象。而不是我创建了一个包含字典列表的数组。我从上面用didChangeObject
方法填写这些词典。这样我就不会在视图控制器中触摸托管对象了。
然后我明白问题出在NSFetchedResultsController中,可能会一直迭代数据。这是在单独的线程中与我的数据更新冲突的对象。
问题是,如果我有一个带有委托的NSFetchedResultsController(意味着它“监视”数据并一直更新delagate),我如何更新单独线程中的核心数据对象。
NSFetchedResultsControllerDelegate实现:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
if ( self.tabBarController.selectedIndex == 0 ) {
UITableView *tableView = self.tableView;
@try {
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
@catch (NSException * e) {
NSLog(@"Exception in didChangeObject: %@", e);
}
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
if ( self.tabBarController.selectedIndex == 0 ) {
@try {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
@catch (NSException * e) {
NSLog(@"Exception in didChangeSection: %@", e);
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
在表视图数据源方法中,我直接使用托管对象。
答案 0 :(得分:12)
这里有两个不同的问题。首先,如果您遇到变异错误,则意味着您在迭代该集合/数组/关系时改变集合或数组(或关系)。找到你在做的地方并停止这样做。这是唯一的解决方案。
至于您的更新。您的背景NSManagedObjectContext
应定期保存。你的主线程应该正在监听NSManagedObjectContextDidSaveNotification
,当它接收到它时,它会调用主线程上的主NSManagedObjectContext
(因为通知很可能会在后台线程中进入) )-mergeChangesFromContextDidSaveNotification:
通过NSNotification
作为参数。这将导致所有NSFetchedResultController
实例触发其委托方法。
这很简单。
请你回复。在后台线程中更新NSManagedObjectContext时抛出异常。我在两个线程中使用相同的NSManagedObjectContext。应用程序应尽可能接近实时应用程序 - 不断更新,表格应立即更新。我根本不保存 - 我只更新NSManagedObjectContext。我在上面提到的一个问题中已经看到有人用来分隔NSManagedObjectContext的实例,但是一旦他合并了这些更改,他仍然会收到相同的异常。那么,你建议使用2个单独的NSManagedObjectContext?
首先,阅读Apple的文档(或我的书:)中的Core Data中的多线程。
第二,是的,每个线程应该有一个上下文,这是核心数据和多线程的黄金规则之一(另一个是不跨线程传递NSManagedObject
个实例)。这可能是您崩溃的根源,如果不是,它将成为未来崩溃的根源。
我有大量数据,我只更新表中的修改/新建/删除项目。如果我开始保存,那么会损害性能吗?
不,只有更新才会在线程中传播。整个数据存储将不会被重新读取,因此当您将存储分解为较小的块时,它实际上将提高性能,因为您将在主线程上更新UI,以更小的块为止用户界面似乎表现更好。
然而,在应用程序完成之前担心性能是应该避免的预优化。猜测什么会表现良好,什么不会,这通常是一个坏主意。