UITableView 1070中的断言失败

时间:2013-03-26 09:14:38

标签: ios core-data nsfetchedresultscontroller

更新:有时我收到错误消息

2013-03-27 19:23:23.094  *** Assertion failure in -[TableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:1070

2013-03-27 19:23:31.280 [53301:c07] CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
2013-03-27 19:23:31.281 [53301:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

我有一个应用程序从远程服务获取大部分数据,并使用核心数据保留它。 我正在编写一个视图,显示UITableView中具有无限滚动的对象集合 [Offtopic:人们会认为这是相当典型的,但结果至少有20种方法可以做到这一点......]

在我的viewDidLoad我构造了获取请求,具有固定的小限制,0偏移和固定批量大小。我针对该请求发起NSFetchedResultsController,针对缓存名称发起nil,针对nil发起sectionNameKeyPath并调用performFetch。然后我附加了以典型方式从NSFetchedResultsController获取数据的数据源(我只有一个部分)

- (NSInteger) tableView:(UITableView *)tableView
  numberOfRowsInSection:(NSInteger)section
{
    return [[fetchController.resultsController sections][section]
            numberOfObjects];
}

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

到目前为止,我可以看到我的对象从数据库中删除。我们的想法是,用户可以直接看到持久性内容,直到新数据来自网络。

然后,在performBlock背景ManagedObjectContext内(其主UI为ManagedObjectContext作为父)我执行网络提取,将对象转换为核心数据并将其推送到主UI上下文。这也可以。

NSFetchResultsController开始收到有关新对象到达的通知时,问题就开始了。我有一个典型的样板,在几本书和苹果自己的文档中都提到过:

- (void)controller:(NSFetchedResultsController*)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath*)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath*)newIndexPath
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.table insertRowsAtIndexPaths:@[newIndexPath]
                      withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [self.table deleteRowsAtIndexPaths:@[indexPath]
                      withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            [self.delegate
             populateCell:[self.table cellForRowAtIndexPath:indexPath]
             indexPath:indexPath
             ];
            break;
        case NSFetchedResultsChangeMove:
            [self.table deleteRowsAtIndexPaths:@[indexPath]
                      withRowAnimation:UITableViewRowAnimationFade];
            [self.table insertRowsAtIndexPaths:@[newIndexPath]
                      withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

现在事情开始变得怪异了。首先,newIndexPath我得到的是奇怪的:有时它是0,有时它是18,有时它是28.它似乎是非确定性的(我的fetchLimitbatchSize设置为20)

此外,当调用insertRowsAtIndexPath时,会从可可层抛出断言:

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070

不,没有进一步的解释。这不是很了不起吗?

我认为UITableView期望和实际获得的行数或部分数之间存在一些不一致。我有什么建议吗?没有UITableView源代码,我不知道从哪里开始寻找。目前我很想重新实现NSFetchedResultsController我自己的模拟,这样我才能看到实际发生的事情。

1 个答案:

答案 0 :(得分:1)

您是否为NSFetchedResultController实现了其他委托方法?

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

- (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;
  }
}