NSFetchedResultsController のキャッシュにハマった

Book が複数の Page を持ってて、さらに Page が MyFavorite 属性をもっている。こーゆーモデルを考えて絞り込み検索しようとして最初にこんな NSPredicate を定義してみた。

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Page" inManagedObjectContext:context];
[request setEntity:entity];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                               @"myFavorite = %@", [NSNumber numberWithBool:YES]];
[request setPredicate:predicate];

NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
                                          initWithFetchRequest:request 
                                          managedObjectContext:context 
                                          sectionNameKeyPath:nil 
                                          cacheName:"MyFavoritePages"];


Book で絞り込んでないな〜と思って、こんな感じに条件を追加。

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                               @"myFavorite = %@ AND book == %@", [NSNumber numberWithBool:YES], self.page.book];


さてと…と思ったら Book で絞り込めない。あれ? アプリを再起動しても絞り込んだ結果が変わらない。NSPredicate の書き方を見てみたけど良さげ。なんだろ〜???悶々と悩んだ挙げ句、アプリを削除して実行しなおしたら、やっと条件が適用された。キャッシュ?

そーいや、NSFetchedResultsController の cacheName を考えずに設定してたけど、どのタイミングでキャッシュされて、どのタイミングでクリアされるか全然考えていなかった。(T^T)

API 見直すと cacheName についてこんな感じに説明されてる。

initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:
...
name:
The name of the cache file the receiver should use. Pass nil to prevent caching.
Pre-computed section info is cached to a private directory under this name. If Core Data finds a cache stored with this name, it is checked to see if it matches the fetchRequest. If it does, the cache is loaded directly—this avoids the overhead of computing the section and index information. If the cached information doesn’t match the request, the cache is deleted and recomputed when the fetch happens.

NSFetchedResultsController Class Reference

cacheName(=name) で指定した値でプライベート領域に結果を保持しておいて、同じ cacheName で NSFetchedResultsController されたら、そっちを優先的に返すそうな。もしキャッシュしたくなければ nil を渡しておきましょ、と書いてある。よく読まないとダメだなぁ。

そーいや、お勉強サイトにさせてもらっている Cocoa の日々さんでも cacheName の説明で注意しましょーってブログを見た記憶が ... あった。思い出すの、遅すぎ (>_<) !!!