CoreData でリレーションシップ(関連)を扱う必要が出てきたのでこれから調査していく。
※今回 MacOSX10.5で検証。
まずはこんなモデルを作ってみた。
BlogEntry.h
@interface BlogEntry :  NSManagedObject  
{
}
@property (retain) NSDate * created;
@property (retain) NSString * title;
@property (retain) NSString * content;
@property (retain) NSSet* tags;
@end
@interface BlogEntry (CoreDataGeneratedAccessors)
- (void)addTagsObject:(NSManagedObject *)value;
- (void)removeTagsObject:(NSManagedObject *)value;
- (void)addTags:(NSSet *)value;
- (void)removeTags:(NSSet *)value;
@end
Tag.h
@interface Tag :  NSManagedObject  
{
}
@property (retain) NSString * name;
@end
テストデータはプログラムで詰め込む。
- (void)addTestData
{
 if ([self getCount]) {
  return;
 }
 NSManagedObjectContext* moc = [self managedObjectContext];
 Tag* tag1 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
             inManagedObjectContext:moc];
 tag1.name = @"Mac";
 Tag* tag2 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
             inManagedObjectContext:moc];
 tag2.name = @"iPhone";
 Tag* tag3 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
             inManagedObjectContext:moc];
 tag3.name = @"iPad";
 BlogEntry* blog1 = [NSEntityDescription insertNewObjectForEntityForName:@"BlogEntry"
              inManagedObjectContext:moc];
 blog1.title = @"CoreData のリレーションしっぷについて";
 blog1.content = @"かくかくしかじか";
 blog1.created = [NSDate date];
 blog1.tags = [NSSet setWithObjects:tag1, tag2, tag3, nil];
 BlogEntry* blog2 = [NSEntityDescription insertNewObjectForEntityForName:@"BlogEntry"
              inManagedObjectContext:moc];
 blog2.title = @"iPad 5/28発売";
 blog2.content = @"かくかくしかじか";
 blog2.created = [NSDate date];
 blog2.tags = [NSSet setWithObjects:tag3, nil];
 NSError* error = nil;
 [moc save:&error];
 if (error) {
  NSLog(@"INSERT ERROR: %@", error);
 } else {
  NSLog(@"INSERTED");
 } 
}
これをフェッチしてみる。ログを追いやすい様に仕切りを入れてある。
- (void)fetchData
{
 NSManagedObjectContext* moc = [self managedObjectContext];
 NSFetchRequest* request = [[NSFetchRequest alloc] init];
 [request setEntity:[NSEntityDescription entityForName:@"BlogEntry"
           inManagedObjectContext:managedObjectContext]];
 NSError* error = nil;
 NSLog(@"----- executeFetchRequest ------------------------------------------");
 NSArray* list = [moc executeFetchRequest:request error:&error];
 NSLog(@"----- listup tag ---------------------------------------------------");
 for (BlogEntry* entry in list) {
  for (Tag* tag in entry.tags) {
   NSLog(@"*tag*: %@", tag.name);
  }
 }
 [request release]; 
}
さて実行してみる。設定で実行SQLもログに出るようにしてある。
[13883:10b] CoreData: sql: pragma cache_size=1000
[13883:10b] CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
[13883:10b] CoreData: sql: SELECT COUNT(*) FROM ZBLOGENTRY t0
[13883:10b] CoreData: annotation: total count request execution time: 0.0022s for count of 2.
[13883:10b] ----- executeFetchRequest ------------------------------------------
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZTITLE, t0.ZCONTENT, t0.ZCREATED FROM ZBLOGENTRY t0
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0013s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0033s for 2 rows.
[13883:10b] ----- fetch tags ---------------------------------------------------
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK FROM ZTAG t0 WHERE t0.Z1TAGS = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0017s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0034s for 2 rows.
[13883:10b] CoreData: annotation: to-many relationship fault "tags" for objectID 0x17bfe0
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0015s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0037s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17dd40
[13883:10b] ** iPhone
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0013s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0023s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17c5b0
[13883:10b] ** Mac
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK FROM ZTAG t0 WHERE t0.Z1TAGS = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0010s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0020s for 1 rows.
[13883:10b] CoreData: annotation: to-many relationship fault "tags" for objectID 0x17bff0
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0012s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0116s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17d000
[13883:10b] ** iPad
わかりずらいが、まとめると次のようになる。
- BlogEntry を取得した段階では Tagは取得されない(fault状態)
- Tag への参照が発生した段階で、SELECTが発行される
なるほど。データのフェッチは必要最低限になるようだ。これはいい。
ソースコード:
CoreDataRelationship at 2010-05-17 from xcatsan's SampleCode - GitHub
(続く)

 
