CoreData&FetchedResultsでデータが変わっても変化しないリストを作る方法

CoreDataを使って開封・未開封のデータを検索→tableViewで一覧表示を作りました。 そのとき、

  1. 未開封のデータを検索
  2. tableViewで一覧表示
  3. そのうちとあるデータを選択
  4. 中身を見る(=開封される)
  5. もどる
  6. tableViewから開封済になったデータが消える

という現象が置きました。 まぁ、FetchedResultsを使っているので、データが開封されればtableViewから消えるのは当然です。 が、検索結果が知らないうちに変化してしまうというのはなんか気持ち悪いので、変化しないようにして見ました。

変化しないPredicate(条件)を作る

どうやっていいか悩んでもわからなかったので@iphone_dev_jpで教えてもらったのですが、 開封、未開封を条件にしてfetchをするから変化してしまうわけで、 変化しない値であるobjectIDを条件にすればいいと教わりました。 つまり、

  1. 未開封のデータを取得し、objectIDのリストを作る
  2. objectIDのリストをもとにfetchedResultsを作る

とすれば問題ない、ということです。

objectIdのリストを作る

ではObjectIDのリストをつくろう、と思ったときに ちょっとだけメモリが気になるのでManagedObjectを通さずに、 CoreDataから直接引っ張ってくることにしました。 手順としてはこんな感じです。

  1. propertiesToFetchでObjectIDを指定
  2. NSDictionaryResultesTypeでデータ取得
  3. NSArrayの中にNSDictionaryがたくさん入ったデータが取得できるので、NSArrayのリストに変換する

具体的には、データ取得までは

1
NSFetchRequest \*request = ...; [request setResultType:NSDictionaryResultType]; NSExpressionDescription\* objectIdDesc = [[[NSExpressionDescription alloc] init] autorelease]; objectIdDesc.name = @"objectID"; objectIdDesc.expression = [NSExpression expressionForEvaluatedObject]; objectIdDesc.expressionResultType = NSObjectIDAttributeType; [request setPropertiesToFetch:[NSArray arrayWithObject:objectIdDesc]]; NSArray *fetchResults = [_context executeFetchRequest:request error:&error]; if (fetchResults == nil) { // Handle the error. NSLog(@"fetch error."); }

このとき、取得したデータはNSArray→NSDictionary→ObjectIdとなってるわけです。

1
( { objectID = "0x7fb52a0 <x-coredata://.../p14>"; }, { objectID = "0x7fb5460 <x-coredata://.../p13>"; } )

これを深い階層のNSArray・NSDictionaryの値から素早く検索するを利用して、NSArray→objectIDにします。

1
NSArray *ids = [fetchResults valueForKeyPath:@"objectID"];
1
( "0x7f9b560 <x-coredata://.../p14>", "0x7f9a870 <x-coredata://.../p13>" )

これでObjectIdのリストが出来ました。

ObjectIdのリストを使ってfetcheResultsを作る

あとはこのObjectIDを使ってFetchedResultsを作るだけです。 このとき注意するのは、PredicateではobjectIdと書かずにselfと書くことです。

1
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@", ids];