NSFetchedResultsController を使うときの注意

この記事は2012年2月20日に書かれたものです。
現在は内容が古い可能性が高いのでご注意ください。

UITableView のデータソースに NSFetchedResultsController を組み合わせて使うときの注意。

何もしていない(delegate 設定しただけ)と、NSManagedObjectContext の deleteObject: を使ってコンテキストから大量のオブジェクトを削除したときに、fetchedObjects の数が減らないので、削除されたオブジェクトを参照しようとして例外を吐いてた。

削除からの流れを下記のようにしようとすると、6の中で NSFetchedResultsController の objectAtIndexPath: を使ったとき毎回こけてた。

1. NSManagedObjectContext の deleteObject: メッセージでオブジェクトを削除
2. NSManagedObjectContext の save: メッセージでオブジェクトの削除操作をコミット
3. NSFetchedResultsController がオブジェクトの操作を検出して controllerDidChangeContent: あたりの delegate メソッドが実行される
4. 3 のメソッド内で UITableView の reloadData メッセージを発行する
5. オーバーライドしてある UITableView の numberOfSections、numberOfRowsInSection: が実行される
6. UITableViewDelegate の tableView:heightForRowAtIndexPath: が実行される
7. オーバーライドしてある UITableView の cellForRowAtIndexPath: が実行される

原因は5で fetchedObjects の count を返しているんだけどこの値がオブジェクトが削除された後の値に更新されていなかったため。テーブルの行数とオブジェクトの数が合わなくなって、削除されたオブジェクトにアクセスしようとしてこけてた。

そのため、下記のように変更する必要がある。(Class Reference もっとちゃんと読んでおけばよかった・・・)

1. NSFetchedResultsController の delegate を無効にしておく
2. NSManagedObjectContext の deleteObject: メッセージでオブジェクトを削除
3. NSManagedObjectContext の save: メッセージでオブジェクトの削除操作をコミット
4. performFetch: で NSFetchedResultsController の状態をリセット
5.自分で UITableView の reloadData メッセージを発行する

ど~も NSFetchedResultsController の動作には癖がある。けど便利なので愛と理解をもって使うことにする。

シェアする

フォローする