NSFetchedResultsControllers controllerDidChangeContent方法中的无限循环

时间:2014-08-03 09:47:15

标签: ios uitableview core-data nsfetchedresultscontroller infinite-loop

我有UITableViewController表,并使用NSFetchedResultsController从Core Data中获取行。我的tableView负责分层数据。 Eatch行位置表示层(零行最高层,n行最低)。假设我们有名为Layers *layers的对象。当我创建新图层时,需要转到表格中的第1行(indexPath.row == 0)并需要获取layers.layer.position = 0。当我创建新图层或进行其他一些更改(移动,删除)时,我需要刷新Core数据对象layers(每个图层都需要获取新的位置值)。但是如果我通过调用[self updateLayersPosition]方法的controllerDidChangeContent方法来刷新图层位置,它会进入无限循环,因为每次在每个旧图层值上更改它会一次又一次地返回到相同的controllerDidChangeContent方法。所以问题是如何"绑定"使用indexPath.row表格行或获取的控制器layers.layer.position?也许我可以知道如何阻止fetchedresultscontroller获取,进行更新然后再次开始获取?或者也许有其他方法可以做到这一点?

以下是代码:

-(void)updateLayers{
    for (int i = 0; i < [self.tableView numberOfRowsInSection:0]; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i 
                                                    inSection:0];
            ((Layer *)[self.fetchedResultsController objectAtIndexPath:indexPath]).layer.position = i;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self updateLayers];
    [self.tableView endUpdates];
}

1 个答案:

答案 0 :(得分:2)

修改Core Data对象后,将调用willChange中的所有didChange / NSFetchedResultsControllerDelegate方法。因此,如果您修改NSManagedObject内的任何controllerDidChangeContent:,它将再次触发controllerDidChangeContent:,从而导致无限循环。

您可以将这些委托回调视为控制器,说“您已经更改了Core Data模型中的内容,现在让我们更新UI(UITableView)。您应遵循的一般模式是:

  • NSManagedObjects
  • 中进行一项或多项更改(添加,修改,删除)
  • 保存NSManagedObjectContext
  • NSFetchedResultsControllerDelegate的方法中:更新UITableView以反映更改(添加,重新加载,删除表格视图行)

所以你可能想做类似的事情:

//in the method in which you add a new Layer object
- (void)addLayer {
  Layer *layer = [NSEntityDescription insertNewObjectForEntityForName:@"Layer" inManagedObjectContext:context];
  //recalculate the position of all the other layers
  //save NSManagedObjectContext
}

关键是在添加新图层后立即更新每个图层的position,然后保存上下文。然后NSFetchedResultsController将根据需要将图层的位置“绑定”到表中的行(我假设您在获取请求中使用的排序描述符中使用position属性)。如果您按照documentation中的建议实施这些NSFetchedResultsControllerDelegate方法,则应该完成这项工作:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (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:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
                  atIndexPath:indexPath];
            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];
}