11

私のアプリケーションには2つのタブバーがあります...それぞれは、ユーザにアイテムのリストを提示するtableviewcontrollerに行きます。最初のビューでは、ユーザーはデータベースにエントリを記録できます。もう一方のタブ/ビューはデータベースから読み取り、それらのアイテムをユーザーに提示しますが、この2番目のビューからcoreData/persistantストアへの更新は行われません。CoreDataが私を狂わせてしまうエラー... CoreData:重大なアプリケーションエラーです。 NSFetchedResultsControllerの代理人からキャッチされた例外

最初のviewcontrollerから新しい項目を追加すると、ビューに完全に表示されます。しかし、他のタブバーをタップして新しい項目がそのviewcontrollerに表示されると、次のエラーが表示され、新しく追加された項目は表示されません。注:アプリを停止して、それを再実行し、2番目のタブバーをタップして開始すると、新しい項目が正常に表示されるので、モデルが正常に更新されていることがわかります。

*** 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が2つの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]; 

} 

ご提供いただけるヘルプは、非常に高く評価されます。

私はこのサイトを検索してこのエラーの多くのインスタンスを見つけましたが、どれも非常に適合していないようです。また、私は戻ったとしてブレークポイントを設定している私が見ているこのエラーは、実際にアップルのコードでは、既知のバグであることを推測するの言及...

*更新された情報*

を見てきましたコードを作成し、この追加情報を元の質問に編集しています。ユーザーがデータベースに新しい項目を追加すると、rootviewからlistCoursesビューに移行します。追加トランザクションは完璧に機能し、listCourses View UITableViewは完全に更新されます。

同じコアデータモデルからデータを読み込む別のビューをクリックすると、viewcontrollerは次のシーケンスを実行しますが、新しい項目をテーブルビューに追加することは決して終了しません。ここではそれが通過するシーケンスがあります。

シミュレータVC:素晴らしい作品

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

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

    - controllerDidChangeContent: 
     [self.tableView endUpdates]; 

他のViewControllerは、レコードがデータベースに追加された直後に、このシーケンスを実行します。

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メッセージを取得するのViewController 1はありませんが、他の一つはしないのですか?

ここには、欠陥のあるビューコントローラのデリゲートメソッドがあります。

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

} 

おかげで、 フィル

答えて

20

のUITableViewの健全性は、このような作品をチェック:ラインで

を[self.tableView beginUpdates]。 tableViewはtableView:numberOfRowsInSection:delegateメソッドを呼び出します。これは3を返しているようです。[self.tableView endUpdates];それは再びそれを呼び出し、それは4を返すようです。したがって、tableViewは、これらの2行の間に1行挿入することを期待しています。実際、テーブルビューがアサーションに失敗するような行は挿入されていません。 (アサーションメッセージの期待された行数と実際の行数を見ることができます)。

3行から4行に増加すると、NSFetchedResultsControllerが新たに挿入されたコアデータアイテムに正しく認識されていることがわかります。あなたがしなければならないことは、あなたのコントローラ:didChangeObject:atIndexPath:forChangeType:メソッドの開始点にブレークポイントを置いて、アイテムを挿入した後に2番目のタブに切り替えるときにステップインします。実行されているswitch文のNSFetchedResultsChangeInsert:caseが表示されるはずですが、これは明らかに起こっていません。

挿入が行われていない理由を理解できます。そうでなければ、このメソッドを踏んだときに実際に見たことをお知らせください。

ADDように編集:あなたは新しいアイテムがタブ1に挿入されたときに直ちにではなく、そのタブに切り替えるときに第二ビューコントローラでのNSFetchedResultsControllerのデリゲートメソッドが呼び出され

[OK]を、これが2回目ということ(すぐに実行されるはずです)、実際には、タブ2に切り替えたときに発生する他のコアデータ更新通知に実際に応答しています。フェッチされた結果コントローラは、beginUpdates行の古い情報で動作していますここでは結果セットの実際に4つのアイテムはありません3)。 endUpdates行に到達するまでに、フェッチをリフレッシュして予期しない挿入を検出しました。

NSFetchedResultsControllerデリゲートメソッドは、変更を行いながらコントローラーのビューが表示されている間、UIのインプレイスを実際に更新するように設計されています。あなたの場合は、変更を加えて、新しいView Controllerを表示します。あなたが本当に使用しなければならないパターンは、このような何かがそれを行う必要があり、コントローラ2のあなたのviewWillAppearメソッドでテーブルビューをリフレッシュすることです:

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

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

    [self.tableView reloadData]; 
} 

これは、あなたがタブ2に切り替えるたび、それが働いていることを確認しますモデルからの新鮮なデータ。

+1

ありがとう、ロビン!私は通り過ぎて、朝にあなたに知らせる... 1amここ! – phil

+0

すべて、元の質問の最後に更新された情報を参照してください。私はブレークポイントとデバッガの方法で見ているテーブルビューのデリゲートメソッドとコールフローを提供しました。私のvcSimulatorテーブルビューデリゲートメソッドがNSFetchedResultsChangeInsertメッセージを受け取らない理由は何ですか? – phil

+0

上記の編集された回答。 –

関連する問題