3

我有一个应用程序使用 Core Data 来驱动一个UITableViewwith 部分。它使用所有示例项目使用的标准NSFetchedResultsControllerDelegate实现。但是,偶尔(但不可靠)调用[self.tableView endUpdates]会引发以下异常:

CoreData: error: Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  attempt to insert row 3 into section 1, but there are only 3 rows in section 1 after the update with userInfo (null)

之后,表格视图只有当时可见的单元格和其他地方的空白空间(但仍可滚动)。没有什么可以解决它,但重新启动应用程序。我确信应用程序会崩溃,除非它NSFetchedResultsController正在捕获异常。

应用程序重新启动后,导致回调的数据立即出现。果然,第 1 节中只有 3 行(或任何例外情况)。那么为什么Core Data会告诉我插入一个不存在的行呢?

- (void)reloadFetch
{
    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        TULogError(@"Unresolved error %@, %@", error, [error userInfo]);
        [[TCCacheController sharedController] clearData];
    }

    [self.tableView reloadData];
}

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController == nil) {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"FeedItem"];
        fetchRequest.fetchBatchSize = 20;
        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"city = %@", [TCCity currentCity]];
        fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"updatedAt" ascending:NO] ];
        fetchRequest.relationshipKeyPathsForPrefetching = @[
                                                            @"user",
                                                            @"responses",
                                                            ];

        _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                        managedObjectContext:[TCCacheController sharedController].mainContext
                                                                          sectionNameKeyPath:@"day"
                                                                                   cacheName:nil];
        _fetchedResultsController.delegate = self;

        [self reloadFetch];
    }

    return _fetchedResultsController;
}

- (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
{
    switch(type) {
        case NSFetchedResultsChangeInsert: {
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        } case NSFetchedResultsChangeDelete: {
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        } case NSFetchedResultsChangeUpdate: {
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        } case NSFetchedResultsChangeMove: {
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        }
    }
}

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

2 回答 2

1

尝试像这样设置硬编码的 tableViewSection:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
//return [[self.fetchedResultsController sections] count];
}

那为我修好了。

于 2014-04-11T19:32:24.803 回答
1

我遇到了类似的问题,但我将自己的自定义部分添加到从 NSFetchedResultsController 返回的结果中。这样做时,我必须确保在管理返回的结果时调整索引路径。我错过了在 didChangeObject 方法中处理索引路径。

我以非常蛮力的方法实现了更改索引路径,但是在寻找这个问题时,我在这个 SO 帖子中发现了一个非常有用和更优雅的解决方案:NSFetchedResultsController prepend a row or section(参见 JosehpH 的回答)。

于 2013-10-02T13:18:13.460 回答