LoginSignup
17
18

More than 5 years have passed since last update.

NSManagedObjectの永続化タイミングとスレッド間での同期

Last updated at Posted at 2014-07-08

CoreDataについて理解が浅すぎてイマイチよくわからないことが多いので的を絞って調査してみた。

疑問

  • NSManagedObjectはいつ永続化されるのか
  • 子コンテキストで行った更新を親コンテキスト管理下のオブジェクトと同期出来るのか(別スレッドのオブジェクト更新を他のスレッドに伝えたい)

NSManagedObjectはいつ永続化されるのか

Persistent StoreにSQLiteを使用してSQLを出力するように設定し、空のオブジェクトを生成して永続化するタイミングを調査した。

// step 1
EntityObject *entityObject = (EntityObject *) [NSEntityDescription insertNewObjectForEntityForName:@"Entity"
                                                                            inManagedObjectContext:self.managedObjectContext];

// step 2
NSError *error = nil;
[self.managedObjectContext save:&error];

step 1のタイミングでデータベースに接続してメタデータを取得していることが解る。このタイミングでは(当然ながら)データの更新処理、すなわち CREATE , UPDATE , DELETE は行われていない。

2014-07-07 20:07:01.551 CoreDataTest[21062:60b] CoreData: annotation: Connecting to sqlite database file at "/Users/key/Library/Application Support/iPhone Simulator/7.1/Applications/E8F1CAD4-5E36-4FEE-8CAC-30838625FAC6/Documents/HogeProj.sqlite"
2014-07-07 20:07:01.552 CoreDataTest[21062:60b] CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'
2014-07-07 20:07:01.553 CoreDataTest[21062:60b] CoreData: sql: pragma journal_mode=wal
2014-07-07 20:07:01.553 CoreDataTest[21062:60b] CoreData: sql: pragma cache_size=200
2014-07-07 20:07:01.554 CoreDataTest[21062:60b] CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA

step 2で明示的に保存処理を行っている。この辞典で同期的にSQLiteへの永続化処理が実施された(INSERT文)。

2014-07-07 20:07:26.641 CoreDataTest[21062:60b] CoreData: sql: BEGIN EXCLUSIVE
2014-07-07 20:07:26.642 CoreDataTest[21062:60b] CoreData: sql: SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?
2014-07-07 20:07:26.642 CoreDataTest[21062:60b] CoreData: sql: UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?
2014-07-07 20:07:26.642 CoreDataTest[21062:60b] CoreData: sql: COMMIT
2014-07-07 20:07:26.643 CoreDataTest[21062:60b] CoreData: sql: BEGIN EXCLUSIVE
2014-07-07 20:07:26.643 CoreDataTest[21062:60b] CoreData: sql: INSERT INTO ZENTITY(Z_PK, Z_ENT, Z_OPT, ZNAME, ZTITLE) VALUES(?, ?, ?, ?, ?)
2014-07-07 20:07:26.644 CoreDataTest[21062:60b] CoreData: sql: COMMIT
2014-07-07 20:07:26.644 CoreDataTest[21062:60b] CoreData: sql: pragma page_count
2014-07-07 20:07:26.644 CoreDataTest[21062:60b] CoreData: annotation: sql execution time: 0.0003s
2014-07-07 20:07:26.645 CoreDataTest[21062:60b] CoreData: sql: pragma freelist_count
2014-07-07 20:07:26.645 CoreDataTest[21062:60b] CoreData: annotation: sql execution time: 0.0003s

子コンテキストで行った更新を親コンテキスト管理下のオブジェクトと同期出来るのか

子コンテキストで行ったNSManagedObjectへの変更は親コンテキストの管理するオブジェクトと同期されるのか確認してみる(self.managedObjectContextなどは適宜読み替えてください)。

// step 1 entityObjectを生成する
self.entityObject = (EntityObject *) [NSEntityDescription insertNewObjectForEntityForName:@"Entity"
                                                                   inManagedObjectContext:self.managedObjectContext];
NSLog(@"entity object: %@", weakSelf.entityObject);

__weak typeof(self) weakSelf = self;

// 別スレッドでオブジェクトを更新
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    // スレッド内で子コンテキストの生成
    NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    localContext.parentContext = weakSelf.managedObjectContext;

    // objectIDを元にEntityObjectを再取得
    EntityObject *localEntityObject = (EntityObject *) [localContext objectWithID:weakSelf.entityObject.objectID];
    localEntityObject.name = @"name";
    localEntityObject.title = @"title";

    // step 2
    NSLog(@"local entity object: %@", localEntityObject);
    NSLog(@"before save: %@", weakSelf.entityObject);

    // step 3
    NSError *localError;
    [localContext save:&localError];

    NSLog(@"after save: %@", weakSelf.entityObject);
});

step 1のself.entityObjectは生成したばかりなのでプロパティは空っぽ。

2014-07-08 11:52:43.245 CoreDataTest[32428:60b] entity object: <EntityObject: 0x926eee0> (entity: Entity; id: 0x926ef10 <x-coredata:///Entity/tC8FA11C7-A73F-463C-8C5D-E18E64716C932> ; data: {
    name = nil;
    title = nil;
})

step 2は別スレッドで実行されており、self.entityObject のobjectIDを元に localEntityObject を取得して、どのように更新されるかを確認する。

localEntityObjectは前行で処理している値が代入されている。

2014-07-08 11:53:09.141 CoreDataTest[32428:60b] local entity object: <EntityObject: 0x8e70840> (entity: Entity; id: 0x926ef10 <x-coredata:///Entity/tC8FA11C7-A73F-463C-8C5D-E18E64716C932> ; data: {
    name = name;
    title = title;
})

self.entityObjectは空のまま。

2014-07-08 11:53:09.798 CoreDataTest[32428:60b] before save: <EntityObject: 0x926eee0> (entity: Entity; id: 0x926ef10 <x-coredata:///Entity/tC8FA11C7-A73F-463C-8C5D-E18E64716C932> ; data: {
    name = nil;
    title = nil;
})

step 3でNSManagedObjectContext:save:を実行すると、self.entityObjectの値が更新される。 なお、このタイミングでは永続化処理が行われない(INSERT, UPDATE文が流れない)。

2014-07-08 11:53:32.064 CoreDataTest[32428:60b] after save: <EntityObject: 0x926eee0> (entity: Entity; id: 0x926ef10 <x-coredata:///Entity/tC8FA11C7-A73F-463C-8C5D-E18E64716C932> ; data: {
    name = name;
    title = title;
})

まとめ

  • NSManagedObjectContext:save:を呼んだタイミングで永続化される
  • 子となるNSManagedObjectContextでNSManagedObjectContext:save:を実行しても、永続化ストアに保存されない
  • 子となるNSManagedObjectContextでNSManagedObjectContext:save:を実行すると、親のNSManagedObjectContextが管理するNSManagedObjectの値が同期する

感想

子となるNSManagedObjectContextで保存しても永続化されないのは新発見(考えてみれば別スレッドで行われた更新が永続化されないのは当然な気がするけど、SQLクエリレベルで追いかけたら理解が深まった)。

NSManagedObjectContext:refreshObject:mergeChanges:がどういう動作をするのかまだ分かっていない(公式のドキュメントを仔細に読めって話かもですが)。

MagicalRecordを使用した場合、調査したような挙動をしないのでこちらは個別に調査しようと思います。


参考

17
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
18