核心数据表视图删除崩溃
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];
根据其他问题,我正确地使用了beginUpdates
和endUpdates
。这是一个令人费解的错误。感谢您的帮助。
【问题讨论】:
【参考方案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 语句。到目前为止,这对这个应用程序有效!
【讨论】:
以上是关于核心数据表视图删除崩溃的主要内容,如果未能解决你的问题,请参考以下文章
当我删除核心数据实体的最后一条记录时,为啥我的应用程序崩溃了?