【发布时间】:2011-12-12 06:14:30
【问题描述】:
我的应用程序有两个标签栏...每个标签栏都将用户带到一个 tableviewcontroller,它为他提供一个项目列表。第一个视图让用户在数据库中记录条目。另一个选项卡/视图从数据库中读取并将这些项目也呈现给用户,但是,不会从第二个视图对 coreData/persistant 存储进行更新。
当我通过第一个视图控制器添加新项目时,它会完美地显示在视图中。但是,当我点击另一个选项卡栏以查看新项目出现在该视图控制器中时,我得到下面列出的错误,并且新添加的项目没有出现......注意:如果我停止应用程序并重新加载/重新运行它,然后通过点击第二个标签栏开始,新项目显示正常,所以我知道模型正在正常更新。
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046
2011-10-20 20:56:15.117 Gtrac[72773:fb03] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. 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 (3), plus or minus the number of rows inserted or deleted from that section (0 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)
委托应用程序的代码,其中 managedObjectContext 被传递给两个 viewController。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// get a point to the master database context
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
// create tab bar controller and array to hold each of the tab-based view controllers that will appear as icons at bottom of screen
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:5];
//
// setup first tab bar item
//
//
// alloc the main view controller - the one that will be the first one shown in the navigation control
RootViewController *rootViewController = [[RootViewController alloc] initWithTabBar];
// Pass the managed object context to the view controller.
rootViewController.managedObjectContext = context;
// create the navigation control and stuff the rootcontroller inside it
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
// set the master navigation control
self.navigationController = aNavigationController;
// add the navigaton controller as the first tab for the tab bar
[localControllersArray addObject:aNavigationController];
[rootViewController release];
[aNavigationController release];
//
// setup the other tab bar
//
//
// alloc the view controller
vcSimulator *vcSimulatorController = [[vcSimulator alloc] initWithTabBar];
UINavigationController *blocalNavigationController = [[UINavigationController alloc] initWithRootViewController:vcSimulatorController];
// Pass the managed object context to the view controller.
vcSimulatorController.managedObjectContext = context;
// add this controller to the array of controllers we are building
[localControllersArray addObject:blocalNavigationController];
// release these guys, they are safely stored in the array - kill these extra references
[blocalNavigationController release];
[vcSimulatorController release];
//
//
// ok, all the tab bars are in the array - get crackin
//
//
// load up our tab bar controller with the view controllers
tabBarController.viewControllers = localControllersArray;
// release the array because the tab bar controller now has it
[localControllersArray release];
[window addSubview:[tabBarController view]];
[window makeKeyAndVisible];
return YES;
When I add a new item via the first viewcontroller, it shows up perfectly in the view. However, as soon as I tap on the other tab bar to see the new item appear in that viewcontroller, I get the error listed above, and the newly added item does not appear... Note: if I stop the app and reload/re-run it, and start by tapping the 2nd tabbar, the new item shows up fine, so I know the model is being updated fine.
Here are the tableview delegate methods from the 2nd view controller.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
您能提供的任何帮助将不胜感激。
我已经搜索了这个网站,发现了很多这个错误的实例,但似乎没有一个完全适合。我还看到引用推断我看到的这个错误实际上是 Apple 代码中的一个已知错误......
* 更新信息 *
我已返回并在代码中设置断点,并正在使用这些附加信息编辑原始问题。当用户向数据库添加新项目时,他会从根视图转换到 listCourses 视图。添加事务完美运行,listCourses View UITableView 完美更新。
当我单击另一个从同一核心数据模型读取数据的视图时,它的视图控制器会按照以下顺序运行,但永远不会结束将新项目添加到 tableview。这是它经历的顺序。
模拟器 VC:
- controllerWillChangeContent which runs...
[self.tableView beginUpdates];
- didChangeObject
..with message: NSFetchedResultsChangeUpdate
..which ran:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
- controllerDidChangeContent:
[self.tableView endUpdates];
另一个很好用的视图控制器,在记录添加到数据库后立即执行此序列。
ListCourses VC:
- didChangeSection
...with message: NSFetchedResultsChangeInsert
...which ran:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
- didChangeObject
..with message: NSFetchedResultsChangeInsert
..which ran:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
为什么一个视图控制器会收到 NSFetchedResultsChangeInsert 消息,而另一个却没有?
这是来自故障视图控制器的委托方法。
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
//[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
//[tableView reloadData];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
// Reloading the section inserts a new row and ensures that titles are updated appropriately.
// [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
NSLog(@"vc>>> about to reload data");
// [self.tableView reloadData];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
// [self.tableView reloadData];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
NSLog(@">>> Entering %s [Line %d] ", __PRETTY_FUNCTION__, __LINE__);
[self.tableView endUpdates];
}
谢谢, 菲尔
【问题讨论】:
标签: iphone uitableview core-data nsfetchedresultscontroller nsmanagedobject