Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
18
Help us understand the problem. What is going on with this article?
@key

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

More than 5 years have passed since last update.

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を使用した場合、調査したような挙動をしないのでこちらは個別に調査しようと思います。


参考

18
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
key
Python + Djangoでウェブ・アプリケーション開発、SwiftでiOSアプリ開発をやってます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
18
Help us understand the problem. What is going on with this article?