【问题标题】:Core Data Table View Deleting Crash核心数据表视图删除崩溃
【发布时间】:2012-07-30 03:47:25
【问题描述】:

我在我的 iPhone 应用程序中设置了父子核心数据关系。我有一个制造商对象和一个汽车对象。它是与制造商作为所有者的一对多关系。主视图是包含制造商的表视图。详细视图是另一个表视图,其中包含不同类型的汽车。我一直使用Tim Roadley's Core Data Tutorial 作为基础。本教程使用Stanford's Core Data Table View Library 作为表格视图。

添加汽车和制造商对我没有任何问题,但是当我进入并删除表格视图中的多辆汽车时,我收到此错误:

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

2012-07-29 23:39:33.561 App [16368:c07] * 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效. 更新后现有节中包含的行数 (0) 必须等于更新前该节中包含的行数 (2),加上或减去从该节中插入或删除的行数 (0已插入,1 已删除)并加上或减去移入或移出该部分的行数(0 移入,0 移出)。'

如果我删除了唯一的汽车,它可以正常工作,直到我尝试添加一辆新车,当我收到此错误时:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'manufacturer' between objects in different contexts (source = <Car: 0x6d96da0> (entity: Car; id: 0x6d8a3c0 <x-coredata:///Car/tC78E17EB-1D68-4998-8C4D-6D1199CE253F4> ; data: {
dateAdded = nil;
manufacturer = nil;
carName = new;
}) , destination = <Manufacturer: 0x6bb1f40> (entity: Manufacturer; id: 0x6d87340 <x-coredata://2E8DDF34-B01A-4203-A53E-73DBE6A2F976/Garden/p6> ; data: <fault>))'

这是我的编辑方法:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{

if (editingStyle == UITableViewCellEditingStyleDelete) {

    [self.tableView beginUpdates];

    Plant *plantToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
    NSLog(@"Deleting plant '%@'", plantToDelete.plantName);
    [self.managedObjectContext deleteObject:plantToDelete];
    [self.managedObjectContext save:nil];

    //delete empty tableview row
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
    NSLog(@"Before performFetch...");
    [self performFetch];
    NSLog(@"After performFetch...");

    [self.tableView endUpdates];
}
}

performFetch 方法包含在前面提到的CoreDataTableViewController 文件中。为了您的方便,这里是:

(void)performFetch
{
_debug = YES;

if (self.fetchedResultsController) {
    if (self.fetchedResultsController.fetchRequest.predicate) {
        if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
    } else {
        if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
    }
    NSError *error;
    [self.fetchedResultsController performFetch:&error];
    if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else {
    if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.tableView reloadData];

}

根据其他问题,我正确地使用了beginUpdatesendUpdates。这是一个令人费解的错误。感谢您的帮助。

【问题讨论】:

    标签: objective-c ios cocoa-touch uitableview core-data


    【解决方案1】:

    我不确定您为什么要再次执行提取,如果从上下文中删除对象,则提取结果控制器已经知道该更改。我认为您遇到的主要问题是在处理表的更新过程中调用 perform fetch。如果你把那行注释掉,它仍然有错误吗?

    此外,以下可能是也可能不是问题的另一部分,因为这是您与我自己的代码不同的地方:

    我之前没有在 tableView:CommitEditingStyle: 中看到开始/结束编辑调用。我自己在该方法中的过程通常会删除对象,而不用担心表行。表行在 fetchedResultController 委托方法中进行协调,如下所示:

       -(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
        //the fetch controller is about to start sending change notifications so prepare the tableview
        [self.tableView beginUpdates];
    }
    
    
    -(void)controller:(NSFetchedResultsController *)controller 
      didChangeObject:(id)anObject 
          atIndexPath:(NSIndexPath *)indexPath 
        forChangeType:(NSFetchedResultsChangeType)type 
         newIndexPath:(NSIndexPath *)newIndexPath {
    
        // reconcile your rows here
        switch(type) {
         case NSFetchedResultsChangeInsert:
    
           break;
    
         case NSFetchedResultsChangeDelete:
           // this one is you
           [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
           break;
    
         case NSFetchedResultsChangeUpdate:
    
           break;
    
         case NSFetchedResultsChangeMove:
    
           break;
    }
    
    -(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
        // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
        [self.tableView endUpdates];
    }
    

    只要行数与获取的对象数匹配,就不会出现该错误。

    【讨论】:

    • 我下载了你基于你的项目的项目。我看到你在这里所拥有的几乎与它相同。它在那里工作,虽然我看到一些对我来说毫无意义的模式。如果您以该项目为基础,则应该仔细检查自己的不同之处。错误消息提到“'非法尝试在不同上下文中的对象之间建立关系'制造商'”。你有多个上下文吗?那肯定会做到的。
    【解决方案2】:

    尝试删除线条

    //delete empty tableview row
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
    NSLog(@"Before performFetch...");
    [self performFetch];
    NSLog(@"After performFetch...");
    

    我相信 CoreDataTableViewController 会自动处理从表中删除行。您实际上是在两次删除导致错误的行。

    【讨论】:

      【解决方案3】:

      当我打开作为 Tim Roadley 的 CoreDataTableViewController 子类的 tableViewController 时,我遇到了同样的错误。我的特定应用程序不要求用户能够添加或删除行,但它允许他们按名称和距离重新排序获取的结果以及搜索数据。我使用了 Dean David 的答案(上面接受的答案),但在每个 case 语句之后,我只添加了一个 break 语句。到目前为止,这对这个应用程序有效!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 1970-01-01
        相关资源
        最近更新 更多