NSFetchedResultsController:删除 1 行使应用程序崩溃

Posted

技术标签:

【中文标题】NSFetchedResultsController:删除 1 行使应用程序崩溃【英文标题】:NSFetchedResultsController: delete 1 row crashes the app 【发布时间】:2014-01-19 21:22:23 【问题描述】:

从由 NSFetchedResultsController 提供的 UITableView 中删除一行会导致我的应用崩溃。

错误是:

* -[UITableView _endCellAnimationsWithContext:] 中的断言失败,/SourceCache/UIKit/UIKit-2903.23/UITableView.m:1330 * 由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'无效更新:第0节中的行数无效。更新后现有节中包含的行数(1)必须等于数字更新前该节中包含的行数 (1),加上或减去从该节插入或删除的行数(0 插入,1 删除),加上或减去移入或移出该节的行数(0迁入,0迁出)。'

我只想滑动删除。我的删除代码是这样的:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  
    [self.tableView beginUpdates];  
    SequenceData *sd = [self.fetchedResultsController objectAtIndexPath:indexPath];  
    [self.managedObjectContext deleteObject:sd];  
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                          withRowAnimation:UITableViewRowAnimationFade];  
    [self.tableView endUpdates];  

我的 NSFetchedResultsController 的设置与 Ray Wanderlich 的教程 (http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller) 中的一样

行数取决于:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section     
    id sectionObjects = [[self.fetchedResultsController sections] objectAtIndex:section];  
    NSInteger nbObjects = [sectionObjects numberOfObjects];  
    return nbObjects;  

看起来 fetch 没有更新(行数没有变化)。我需要自己获取(这不是由获取控制器处理的)吗?

显然我在这里缺少一些基本的东西。不要犹豫,提出基本的答案。

我首先用控制器实现了这个:didChangeObject: ... 方法。错误是一样的,但有些细节不同。

编辑 我相信我的问题已经解决了。两个答案(来自 CX 和 Martin)都帮助我找到了答案。马丁得到了答案,因为这些解释帮助我更好地理解了一点......

【问题讨论】:

【参考方案1】:

不要打电话

[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

如果您使用获取的结果控制器。只用

删除对象
[self.managedObjectContext deleteObject:sd];

获取结果控制器委托方法然后自动调用didChangeObject:, 并调用deleteRowsAtIndexPaths:

因此,在您的情况下,该行被删除了两次,这导致了异常。

请注意,这里不需要beginUpdates/endUpdates

【讨论】:

谢谢。实施您的答案不会再使应用程序崩溃,但不会从表视图中删除该行。但是数据被删除了。我需要任何类型的 didChangeObject 实现吗? @invalidArgument:是的,您必须实现 4 个获取结果控制器委托方法。看看“还有一件事——我们需要实现 NSFetchedResultsController 的委托方法” 和教程中的以下代码。 - 它也记录在developer.apple.com/library/ios/documentation/CoreData/…。 实施这些方法会使我的应用程序再次崩溃。所以在commitEditingStyle 中,我只是删除了对象。在didChangeObject 中,它是教程中的复制粘贴(因此在此处有deleteRowsAtIndexPaths)。该行实际上引发了异常,indexPath 和 newIndexPath 均为零。 (我还加了controllerWillChanteContentcontrollerDidChangeContent @invalidArgument:这很奇怪,indexPath 在 NSFetchedResultsChangeDelete 情况下不应该为 nil。【参考方案2】:

如果你想删除一行,你只需要删除 managedObject。

if (editingStyle == UITableViewCellEditingStyleDelete)

    [self.managedObjectContext deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

    NSError *error = nil;
    if (![self.managedObjectContext save:&error]) 
        // handle error
    

删除托管对象会触发NSFetchResultController 委托方法,它们将更新tableView

编辑

你应该实现 NSFetchResultController 委托方法

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
switch(type) 
 case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;



////
   default:
   break;
    

因为当您使用 NSFetchedResultsController 之类的数据源时,所有更改都必须来自那里,并且您的表只反映它们。

【讨论】:

谢谢。我对您的建议的回答与我给马丁的回答类似。数据被删除,但行不会消失。我应该寻找什么? 你在实现 NSFetchResultController 的委托吗...? 是的,我是。 controllerWillChangeContentcontroller: didChangeObject: ...controllerDidChangeContent 你用我最新的编辑更新代码了吗,你是在 didChange 还是 willChange 中调用[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];,你应该把它加到一个更好的地方,你要添加 didChange .... [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];controller: didChangeObject:NSFetchedResultsChangeDelete 案例中实现。在willChangedidChange 里面我分别做[self.tableView beginUpdates][self.tableView endUpdates]

以上是关于NSFetchedResultsController:删除 1 行使应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?