首页 文章

iPhone:NSFetchedResultsController,具有来自单独线程的委托和数据更新

提问于
浏览
5

首先,抱歉这个问题太长了 .

我知道这里讨论类似问题的问题很少,但这些问题都没有谈到NSFetchedResultsController和委托以及单独线程中的更新 . 并没有一个解决方案帮助了我 .
这些是现有的问题:

现在关于我的问题:

  • 我有一个单独的线程,用于从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 回答

  • 12

    这里有两个不同的问题首先,如果您遇到变异错误,这意味着您在迭代该集合/数组/关系时改变集合或数组(或关系) . 找到你在做的地方并停止这样做 . 这是唯一的解决方案 .

    至于你的更新 . 您的背景 NSManagedObjectContext 应定期保存 . 你的主线程应该正在监听 NSManagedObjectContextDidSaveNotification ,当它接收到它时,它会通过 -mergeChangesFromContextDidSaveNotification: 调用主线程上的主 NSManagedObjectContext (因为通知很可能会在后台线程中进入),它将 NSNotification 作为参数 . 这将导致所有 NSFetchedResultController 实例触发其委托方法 .

    就那么简单 .

    更新

    谢谢你的回复 . 在后台线程中更新NSManagedObjectContext时抛出异常 . 我在两个线程中使用相同的NSManagedObjectContext . 应用程序应尽可能接近实时应用程序 - 不断更新,表格应立即更新 . 我根本不保存 - 我只更新NSManagedObjectContext . 我在上面提到的一个问题中看到有人曾经分离过NSManagedObjectContext的实例,但是一旦他合并了这些更改,他仍会收到相同的异常 . 那么,你建议使用2个独立的NSManagedObjectContext?

    首先,从Apple的文档(或我的书:)中读取Core Data中的多线程 .

    其次,是的,每个线程应该有一个上下文,这是核心数据和多线程的黄金规则之一(另一个是不跨线程传递 NSManagedObject 实例) . 这可能是您崩溃的根源,如果不是,它将成为未来崩溃的根源 .

    更新

    我有大量的数据,我只更新表中的修改/新/删除项目 . 如果我开始保存那么它会损害性能吗?

    不,只有更新才会在线程中传播 . 整个数据存储将不会被重新读取,因此当您将存储分解为较小的块时,它实际上会提高性能,因为您将在主线程上更新UI,以更小的块,因此UI看起来表现更好 .

    但是,在应用程序完成之前担心性能是应该避免的预优化 . 猜测什么会表现良好,什么不会通常是一个坏主意 .

相关问题