代码之家  ›  专栏  ›  技术社区  ›  Michael Kessler

iPhone:NSFetchedResultsController,从单独的线程进行委托和数据更新

  •  5
  • Michael Kessler  · 技术社区  · 14 年前

    首先,对不起,问题太长了。

    我知道这里很少有问题讨论类似的问题,但是没有一个讨论NSFetchedResultsController和delegate以及单独线程中的更新。但没有一个解决方案对我有帮助。

    • 我有一个单独的线程(使用套接字)从web更新核心数据对象。
    • 很少有视图控制器显示来自同一核心数据对象的数据(每个选项卡都包含一个显示其过滤数据的视图控制器)。
    • 每个视图控制器都有自己的 NSFetchedResultsController 代理被设置为self。

    有时我收到 was mutated while being enumerated 更新单独线程中的数据时出现异常,有时会使应用程序崩溃。

    我做了很多代码操作,以试图修复它,似乎没有什么帮助。
    我试图不直接从表视图数据源方法中使用托管对象。相反,我创建了一个数组来保存字典列表。我把那些词典都填进了表格 didChangeObject 方法。这样,我在视图控制器中根本不接触托管对象。

    然后我了解到问题出在NSFetchedResultsController中,它可能一直在迭代数据。这个对象与我在单独线程中的数据更新冲突。

    问题是,一旦我有了一个带有delegate的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 回复  |  直到 8 年前
        1
  •  12
  •   Marcus S. Zarra    14 年前

    这里有两个独立的问题。首先,如果您得到了变异错误,这意味着您在迭代集合/数组/关系时正在变异集合或数组(或关系)。找出你在做什么,然后停止做。这是唯一的解决办法。

    至于你的最新消息。你的背景 NSManagedObjectContext NSManagedObjectContextDidSaveNotification 他俩 (因为通知很可能会出现在后台线程中)通过 -mergeChangesFromContextDidSaveNotification: NSNotification 作为参数。这将导致你所有的 NSFetchedResultController 实例来激发其委托方法。

    更新

    谢谢你的答复。在后台线程中更新NSManagedObjectContext时引发异常。我在两个线程中使用相同的NSManagedObjectContext。该应用程序应尽可能接近实时应用程序-不断更新和表应立即更新。我根本不保存-我只更新NSManagedObjectContext。我在前面提到的一个问题中看到,有人曾经分离NSManagedObjectContext的实例,但一旦合并了这些更改,他仍然会收到相同的异常。所以,您建议使用两个单独的NSManagedObjectContext?

    NSManagedObject 跨线程的实例)。这可能是你崩溃的根源,如果不是的话,它将是未来崩溃的根源。

    不,只有更新会在线程之间传播。整个数据存储将不会被重新读取,因此它实际上会被重新读取 改善 性能当您将保存拆分为更小的块时,因为您将在主线程上更新UI,所以UI看起来会表现得更好。