1

我在使用带有 Core Data 的 TableView 时遇到了崩溃问题,非常感谢任何帮助。场景是:

  • 我有一个 UITableViewController 显示存储在核心数据中的数据。我使用 NSFetchResultsController 按照文档的指示进行提取。我有一个专用的 NSManagedObjectContext 专用于主线程来获取数据。

  • 数据实际上来自服务器。当我的应用程序启动时,我有一个后台线程来将数据刷新到我的核心数据堆栈中。根据 Apple 的建议,我在后台线程中使用不同的 NSManagedObjectContext 来刷新数据。在刷新期间,旧数据将被删除。

  • 后台保存更改后,我使用 NSManagedObjectContextDidSaveNotification 触发调用以在主线程的上下文中执行 mergeChangesFromContextDidSaveNotification。

  • FRC 的 controllerDidChangeContent 也实现了,它调用 UITableViewController 来重新加载。

一切正常 - 除非我在数据刷新过程中滚动 TableView,否则应用程序将因“Core Data 无法完成故障...”错误而崩溃。通过代码追踪,我认为原因是后台线程保存数据的删除和主线程上下文合并操作之间有一个小的时间延迟。在这段时间延迟期间,主线程上下文中的一些托管对象被删除,因此当滚动表并且数据源方法访问已删除的对象时,应用程序将崩溃。

我的信念正确吗?如果是这样,我应该如何处理这个时间延迟?

非常感谢。

4

2 回答 2

0

嗯,合并完成后没有延迟。数据将我可用。您的问题似乎具有不同的性质。我可以告诉你的第一件事是你的方法有些不正确。您不应该只是为了刷新而删除数据。适当的更新是有效的方法。

话虽如此,这里有几件事需要考虑:

  1. 确保与主线程的托管对象上下文的合并调用正在主线程中完成。如果您不这样做,您的主线程的上下文将在它调用的线程中触发 NSFetchedResultsController 的通知,并且它将在该线程中调用 NSFetchedResultsController 的委托方法,可能会在主线程之外更新您的 UI .
  2. 确保对 NSFetchedResults 委托执行正确的过程。

这是我的实现:

#pragma mark -
#pragma mark  NSFetchedResultsControllerDelegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id   <NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {
    case NSFetchedResultsChangeInsert:
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                      withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [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:
        [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:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeMove:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
于 2012-08-16T21:07:19.827 回答
0

这个我自己没试过,但是看看NSManagedObjectContextWillSaveNotification。我会尝试注册那些来自后台上下文的通知。然后在处理程序中,您可以对主线程进行同步调度并传递上下文的已删除对象 ID:

- (void)handleBackgroundSave:(NSNotification *)note {
    NSManagedObjectContext *context = [note object];
    NSSet *deletedObjectIDs = [[context deletedObjects] valueForKey:@"objectID"];
    dispatch_sync(dispatch_get_main_queue(), ^{
        // deletedObjectIDs can be passed across threads
        // if NSFetchedResultsController's fetchedObjects contains deleted objects
        // you have to disable it and refetch after DidSaveNotification
    });
}

由于调度是同步的,它应该阻止实际删除,直到您在主线程的上下文中处理它。请记住,这未经测试,可能会导致一些令人讨厌的死锁。

值得指出的另一件事是,NSFetchedResultsControllerDelegate当有很多对象发生变化(如数百/数千)时,交互式更新(如在实现中)将占用 UI 线程,因此如果您在刷新期间替换所有核心数据对象,您不妨禁用 frc在每个 WillSave 上并在每个 DidSave 上重新获取。

如果您负担得起以 iOS 5+ 为目标,那么我建议您探索嵌套上下文 -这是一个很好的方法概述

于 2012-09-06T11:41:26.237 回答