首先,抱歉这个问题太长了 .
我知道这里讨论类似问题的问题很少,但这些问题都没有谈到NSFetchedResultsController和委托以及单独线程中的更新 . 并没有一个解决方案帮助了我 .
这些是现有的问题:
-
NSFetchedResultsController: using of NSManagedObjectContext during update brings to crash
-
Determining which core Data attribute/property change triggered a NSFetchedResultsController update
-
Collection was mutated while being enumerated. How to determine which set?
-
等
现在关于我的问题:
-
我有一个单独的线程,用于从Web更新核心数据对象(使用套接字) .
-
很少有视图控制器显示来自同一核心数据对象的数据(每个选项卡包含一个显示其过滤数据的视图控制器) .
-
每个视图控制器都有自己的
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];
}
在表视图数据源方法中,我直接使用托管对象 .
1 回答
这里有两个不同的问题首先,如果您遇到变异错误,这意味着您在迭代该集合/数组/关系时改变集合或数组(或关系) . 找到你在做的地方并停止这样做 . 这是唯一的解决方案 .
至于你的更新 . 您的背景
NSManagedObjectContext
应定期保存 . 你的主线程应该正在监听NSManagedObjectContextDidSaveNotification
,当它接收到它时,它会通过-mergeChangesFromContextDidSaveNotification:
调用主线程上的主NSManagedObjectContext
(因为通知很可能会在后台线程中进入),它将NSNotification
作为参数 . 这将导致所有NSFetchedResultController
实例触发其委托方法 .就那么简单 .
更新
首先,从Apple的文档(或我的书:)中读取Core Data中的多线程 .
其次,是的,每个线程应该有一个上下文,这是核心数据和多线程的黄金规则之一(另一个是不跨线程传递
NSManagedObject
实例) . 这可能是您崩溃的根源,如果不是,它将成为未来崩溃的根源 .更新
不,只有更新才会在线程中传播 . 整个数据存储将不会被重新读取,因此当您将存储分解为较小的块时,它实际上会提高性能,因为您将在主线程上更新UI,以更小的块,因此UI看起来表现更好 .
但是,在应用程序完成之前担心性能是应该避免的预优化 . 猜测什么会表现良好,什么不会通常是一个坏主意 .