核心数据表视图删除崩溃

Posted

技术标签:

【中文标题】核心数据表视图删除崩溃【英文标题】: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。这是一个令人费解的错误。感谢您的帮助。

【问题讨论】:

【参考方案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 语句。到目前为止,这对这个应用程序有效!

【讨论】:

以上是关于核心数据表视图删除崩溃的主要内容,如果未能解决你的问题,请参考以下文章

从 iphone 的核心数据中删除对象时应用程序崩溃

当我删除核心数据实体的最后一条记录时,为啥我的应用程序崩溃了?

将输出映射到 Restkit 中的核心数据时崩溃

核心数据:移动到详细视图时获取崩溃

当图像保存在核心数据的集合视图中时,应用程序因内存错误而崩溃

删除后尝试保存时核心数据崩溃