CoreData错误驱使我疯了... CoreData:严重的应用程序错误。从NSFetchedResultsController的委托捕获到异常 [英] CoreData error driving me crazy... CoreData: Serious application error. An exception caught from delegate of NSFetchedResultsController

查看:135
本文介绍了CoreData错误驱使我疯了... CoreData:严重的应用程序错误。从NSFetchedResultsController的委托捕获到异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序有两个标签栏...每个都会将用户转到一个tableviewcontroller,向他提供一个项目列表。第一个视图允许用户在数据库中记录条目。其他选项卡/视图从数据库读取并将这些项目也显示给用户,但是,不会从该第二个视图更新到coreData / persistant存储。



当我通过第一个viewcontroller添加一个新项目时,它在视图中完全显示。但是,一旦我点击其他标签栏看到新的项目出现在该viewcontroller,我得到下面列出的错误,新添加的项目不会出现...注意:如果我停止应用程序,重新加载/重新运行它,并通过点击第二个标签栏开始,新项目显示正常,所以我知道模型正在更新。

  ***  -   -  [UITableView _endCellAnimationsWithContext:]中的声明失败,/SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046 
2011-10-20 20:56:15.117 Gtrac [72773 :fb03] CoreData:error:严重的应用程序错误。在对-controllerDidChangeContent:的调用期间,NSFetchedResultsController的委托捕获了异常。无效更新:部分0中的行数无效。更新(4)后现有段中包含的行数必须等于更新前该部分中包含的行数(3),加或减数从该部分插入或删除的行(插入0个,删除0个)以及移入或移出该部分的行数(0移入,0移出)。 with userInfo(null)



来自委托应用程序的代码,其中managedObjectContext传递给两个viewController。 / p>

   - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


//获取一个指向master数据库上下文的
NSManagedObjectContext * context = [self managedObjectContext];
if(!context){
//处理错误。
}

//创建标签栏控制器和数组,用于保存将显示为屏幕底部图标的每个基于标签的视图控制器
tabBarController = [[UITabBarController alloc ] 在里面];
NSMutableArray * localControllersArray = [[NSMutableArray alloc] initWithCapacity:5];


//
//设置第一个标签栏项目
//
//
//分配主视图控制器 - 一个将是导航控件中显示的第一个
RootViewController * rootViewController = [[RootViewController alloc] initWithTabBar];

//将受管对象上下文传递给视图控制器。
rootViewController.managedObjectContext = context;

//创建导航控件并在其中包含rootcontroller
UINavigationController * aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];

//设置主导航控件
self.navigationController = aNavigationController;

//将navigaton控制器添加为标签栏的第一个选项卡
[localControllersArray addObject:aNavigationController];

[rootViewController release];
[aNavigationController release];


//
//设置其他标签栏
//
//

//分配视图controller
vcSimulator * vcSimulatorController = [[vcSimulator alloc] initWithTabBar];

UINavigationController * blocalNavigationController = [[UINavigationController alloc] initWithRootViewController:vcSimulatorController];

//将受管对象上下文传递给视图控制器。
vcSimulatorController.managedObjectContext = context;

//将此控制器添加到我们正在构建的控制器数组
[localControllersArray addObject:blocalNavigationController];

//释放这些家伙,他们被安全地存储在数组 - 杀死这些额外的引用
[blocalNavigationController release];
[vcSimulatorController release];


//
//
//确定,所有的标签栏都在数组中 - get crackin
//
/ /
//使用视图控制器加载我们的标签栏控制器
tabBarController.viewControllers = localControllersArray;

//释放数组,因为标签栏控制器现在有
[localControllersArray release];

[window addSubview:[tabBarController view]];
[window makeKeyAndVisible];

return YES;




当我通过第一个视图控制器添加一个新项目时,它在视图中完全显示。但是,一旦我点击其他标签栏看到新的项目出现在该viewcontroller,我得到上面列出的错误,新添加的项目不会出现...注意:如果我停止应用程序,重新加载/重新运行它,并通过点击第二个标签栏开始,新项目显示得很好,所以我知道模型正在更新很好。

下面是来自第二个视图控制器的tableview委托方法。



- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

[self.tableView beginUpdates]; (NSFetchedResultsChangeType)类型newIndexPath:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType) 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];

}

您可以提供的任何帮助将非常感谢。 p>

我搜索了这个网站,发现这个错误的许多实例,但没有一个似乎是非常适合。我还看到引用推断,我看到的这个错误实际上是一个已知的bug在苹果代码...



*更新信息* strong>



我回来了,并在代码中设置断点,并使用这些附加信息编辑原始问题。当用户向数据库添加一个新项目时,它将从rootview转换到listCourses视图。添加事务工作完美无缺,并且listCourses视图UITableView完美更新。



当我点击另一个视图,也从相同的核心数据模型读取数据,它的viewcontroller运行通过以下序列,但从不结束将新项目添加到tableview。



模拟器VC:

  -  controllerWillChangeContent运行... 
[self.tableView beginUpdates];

- didChangeObject
.. with message:NSFetchedResultsChangeUpdate
..其中:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]

- controllerDidChangeContent:
[self.tableView endUpdates];

另一个工作良好的viewcontroller在记录添加到数据库后立即执行此序列。



ListCourses VC:

   -  didChangeSection 
.with message:NSFetchedResultsChangeInsert
...其中:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];

- didChangeObject
.. with message:NSFetchedResultsChangeInsert
..其中:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];

为什么一个viewcontroller获取NSFetchedResultsChangeInsert消息,但另一个没有?



这是来自错误viewcontroller的委托方法。

  //覆盖以支持编辑表视图。 
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(editingStyle == UITableViewCellEditingStyleDelete){
/ /删除数据源中的行
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if(editingStyle == UITableViewCellEditingStyleInsert){
//创建一个相应类的新实例,将其插入数组,并向表视图中添加一个新行
}
}





- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
//提取控制器即将开始发送更改通知,因此请准备表视图以进行更新。
NSLog(@>>>输入%s [Line%d],__PRETTY_FUNCTION__,__LINE__);

[self.tableView beginUpdates]; (NSFetchedResultsChangeType)类型newIndexPath:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType) 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];

//重新加载节插入一个新行,并确保标题已正确更新。
// [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];

break;
}
NSLog(@vc>>>关于重新加载数据);
// [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 {
//获取控制器已发送所有当前更改通知告诉表视图来处理所有更新。
NSLog(@>>>输入%s [行%d],__PRETTY_FUNCTION__,__LINE__);
[self.tableView endUpdates];

}

谢谢,
phil

$ UITableView健全检查的工作原理是这样的:



在行[self.tableView beginUpdates]; tableView调用你的tableView:numberOfRowsInSection:delegate方法,这似乎是返回3.在行[self.tableView endUpdates];它再次调用它,它似乎返回4.因此,tableView希望您在这两行之间插入1行。实际上,没有插入行,因此tableView无法通过断言。 (您可以在断言消息中看到预期和实际的行计数)。



从3行增加到4行表明您的NSFetchedResultsController注意到新插入的Core Data项目。你需要做的是在你的控制器的开头放一个断点:didChangeObject:atIndexPath:forChangeType:方法,并在插入一个项目后切换到第二个选项卡时通过它。你应该看到正在执行switch语句的NSFetchedResultsChangeInsert:case,但这显然不会发生。



希望你能知道为什么插入没有发生 - 否则



已准备添加:



确定,所以当切换到该选项卡而不是立即当新项目插入到选项卡1时,将调用在第二个视图控制器中的NSFetchedResultsController委派方法。这意味着第二个视图控制器没有看到插入(应该立即发生),并且实际上响应稍后切换到标签2时发生的一些其他Core Data更新通知。获取的结果控制器在beginUpdates行使用过期信息(结果集中实际有4个项目不是3)。到达endUpdates行的时候,它刷新了它的fetch并发现了一个意想不到的插入。



NSFetchedResultsController委托方法真正设计用于在就地更新UI,而你正在进行更改,控制器的视图是可见的。在你的情况下,你正在进行更改,然后显示新的视图控制器。你真正应该使用的模式是刷新控制器2的viewWillAppear方法中的tableview。这样应该这样:

   - (void)viewWillAppear:(BOOL)animated 
{
[super viewWillAppear:animated];

NSError * error = nil;
[resultsController performFetch:& error]; // Refetch data
if(error!= nil){
// handle error
}

[self.tableView reloadData];
}

这将确保每当切换到标签2时,来自模型的数据。


My application has two tab bars... Each takes the user to a tableviewcontroller that presents him with a list of items. The first view lets the user record entries in the database. The other tab/view reads from the database and presents those items to the user as well, however, no updates are made to the coreData/persistant store from this second view.

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 below, 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 tab-bar, the new item shows up fine, so I know the model is being updated fine.

*** 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)

Code from the delegate application where the managedObjectContext is passed to the two viewControllers.

- (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];

}

Any help you can provide would be very appreciated.

I have searched this site and found many instances of this error but none seem to quite fit. I've also seen references inferring that this error I'm seeing is actually a known bug in the Apple code...

* UPDATED INFORMATION *

I've gone back and set break points in the code and am editing the original question with this additional information. When the user adds a new item to the database he is transitioned from the rootview to the listCourses view. The add transaction works flawlessly and the listCourses View UITableView is updated perfectly.

When I click on the other view that also reads data from the same core data model, it's viewcontroller runs through the following sequence but never ends adding the new item to the tableview. Here is the sequence it goes through.

Simulator VC:

- controllerWillChangeContent which runs...
        [self.tableView beginUpdates];

    - didChangeObject 
        ..with message: NSFetchedResultsChangeUpdate
        ..which ran:
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] 

    - controllerDidChangeContent:   
        [self.tableView endUpdates];

The other viewcontroller that works great, goes through this sequence immediately after the record is added to the database.

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];

Why does the one viewcontroller get the NSFetchedResultsChangeInsert message but the other one does not?

Here are the delegate methods from the faulty viewcontroller.

// 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];

}

Thanks, phil

解决方案

The UITableView sanity checking works like this:

At the line [self.tableView beginUpdates]; the tableView calls your tableView:numberOfRowsInSection: delegate method, which appears to be returning 3. At the line [self.tableView endUpdates]; it calls it again and it seems to be returning 4. Therefore, the tableView is expecting you to insert 1 row between these two lines. In fact, no rows are being inserted so the tableView fails an assertion. (You can see the expected and actual row counts in the assertion message).

The increase from 3 rows to 4 rows shows that your NSFetchedResultsController is noticing the newly inserted Core Data item correctly. What you need to do is put a breakpoint at the start of your controller:didChangeObject:atIndexPath:forChangeType: method and step through it when you switch to the 2nd tab after inserting an item. You should see the NSFetchedResultsChangeInsert: case of the switch statement being performed but this is obviously not happening.

Hopefully, you can figure out why the insertion is not taking place - otherwise come back and let us know what you actually saw when stepping through this method.

EDITED TO ADD:

OK, so your NSFetchedResultsController delegate methods in the 2nd view controller are called when you switch to that tab rather than immediately when the new item is inserted on tab 1. This means that the 2nd view controller is not seeing the insert (which should happen immediately) and is actually responding to some other Core Data update notification later that occurs when you switch to tab 2. The fetched results controller is working with stale information at the beginUpdates line (there are actually 4 items in the result set here not 3). By the time it gets to the endUpdates line it has refreshed its fetch and found an unexpected insert.

The NSFetchedResultsController delegate methods are really designed to update UI in-place while you are making changes AND the controller's view is visible. In your case, you are making changes and THEN displaying the new view controller. The pattern that you really should be using is to refresh the tableview in your viewWillAppear method of controller 2. Something like this should do it:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSError *error = nil;
    [resultsController performFetch:&error]; // Refetch data
    if (error != nil) {
        // handle error
    }

    [self.tableView reloadData];
}

This will make sure that whenever you switch to tab 2 it is working with fresh data from the model.

这篇关于CoreData错误驱使我疯了... CoreData:严重的应用程序错误。从NSFetchedResultsController的委托捕获到异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆