43
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CoreDataでatomicにsaveする

Last updated at Posted at 2014-08-08

Core Dataは関係データベースでも関係データベース管理システム(RDBMS)でもありません。 (by Apple CoreDataプログラミングガイド)

ORマッパーでもありませんし、永続ストアのひとつとしてたまたまSQLiteを使えるっていうだけで、プライマリーキー制約なんて概念もありません。

なので例えば

User
-uid
-name

こんなモデルがあったとして

[
  { 
    "uid": 1, 
    "name": "kentaro"
  },
  {
     "uid": 2,
     "name": "yamada"
  }
  ...
]

こんなjsonレスポンスを返すAPIがあった場合、

//APIアクセスしてレスポンス取得
NSNumber* uid = ..;
NSString* name = ...

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *ctx) {
    User* user = [User MR_findFirstByAttribute:@"uid" withValue:uid inContext:ctx];
    if (user == nil) {
        user = [User MR_createInContext:ctx];
        user.uid = uid;
    }
    person.name = name;
} completion:^(BOOL success, NSError *error) {
    assert(success);
}];

こういうコードを書いても、とくにクラッシュすることなく普通にうごきます。
ただし、このようなAPIアクセス〜永続化処理が、並列で同時に100回くらい実行されると、、、

int cnt = [Person MR_countOfEntitiesWithPredicate:[NSPredicate predicateWithFormat:@"uid = 1"]];
NSLog(@"count = %d", cnt);

//> count:100

あれー、uid=1のレコードが複数保存されちゃってるー、ってことが起こりえます。
saveWithBlock:ブロック内部がアトミックじゃないからです。

こんな場合は、たとえばGCDの直列ディスパッチキューを一つ用意して、Test and Set を実行するブロックは必ずそのキュー上で実行させてみます。

static dispatch_queue_t serialQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    serialQueue = dispatch_queue_create("net.kenmaz.serialqueue", DISPATCH_QUEUE_SERIAL);
});

dispatch_async(serialQueue, ^{
    //ここから〜
    NSManagedObjectContext* ctx = [NSManagedObjectContext MR_context];

    User* user = [User MR_findFirstByAttribute:@"uid" withValue:uid inContext:ctx];
    if (user == nil) {
        user = [User MR_createInContext:ctx];
        user.uid = uid;
    }
    person.name = name;

    [ctx MR_saveToPersistentStoreAndWait];
    //〜ここまで排他的に実行
});

100並列実行しても・・・

int cnt = [Person MR_countOfEntitiesWithPredicate:[NSPredicate predicateWithFormat:@"uid = 1"]];
NSLog(@"count = %d", cnt);

//> count:1

おkぽ

みなさん、このあたりはどのように書いてますかね?
よいアイデアがあればおせーてください。

ちなみにこのへん↓でも似たような話をしてるっぽい
http://stackoverflow.com/questions/7718801/is-a-gcd-dispatch-queue-enough-to-confine-a-core-data-context-to-a-single-thread

43
46
7

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
43
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?