CoreDataでSerious application error

はじめに

CoreData <-> UITableViewの連携でどつぼにハマりまくって、若干ヤケ気味なゆう@あんのうんです。

事象と解決法

UITableViewの

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath

の中でCoreDataのUpdateを行ったら以下のようなエラーが出て、すっぽりドツボにハマりました

CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  
Invalid update: invalid number of rows in section 0.  
The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (5), 
plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 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)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

色々書いてありますが、簡単に言うと、更新後のUITableViewのメソッドが、CoreDataのfetchedResultsControllerを呼ぶのですが、 それを読み込んだ時点で、ManagedObjectContextと不整合が起きた為、この手のエラーが発生しました。

なので、私が出くわした際は、以下のメソッドの中身を変更しました。

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    case NSFetchedResultsChangeDelete:
            /*処理*/
            break;
    case NSFetchedResultsChangeMove:
        [self.tableView reloadData];
            break;
}

今作っているアプリは、リアルタイムにCellの更新を行わなくても良いものなので、ここでのcell更新を解除し、endUpdate内でreloadDataを行うようにしました。

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    @try {
        [self.tableView endUpdates];
    }
    @catch (NSException *exception) {
        /*error!*/
    }
    @finally {
        
    }
}

リアルタイムverも、取り掛かって見ようと思ったのですが、時間キレなのでここまでで。