再現方法がわかったので追記しました(2016/06/21)
CoreDataにおいて、並列処理違反時に例外を吐いてくれる、-com.apple.CoreData.ConcurrencyDebug 1
というデバッグオプションがあります。
http://qiita.com/hongmhoon/items/606a352b1e96dfb0bec5#デバッグこれが重要
ただしこのオプション、コード上は問題ないはずなのに誤って例外を吐いてしまうケースがあります。
再現例
例えば以下のようなコードがあったとします。
[privateContext performBlock:^{
NSEntityDescription* entADesc = [NSEntityDescription entityForName:@"EntityA" inManagedObjectContext:privateContext];
EntityA* entA = [[EntityA alloc] initWithEntity:entADesc insertIntoManagedObjectContext:privateContext];
entA.foo = @"aaa";
[privateContext save:&error];
NSManagedObjectContext* parentContext = privateContext.parentContext;
[parentContext performBlock:^{
[parentContext save:&error];
}];
}];
//fetch EntityB (asyn on main context)
[mainContext performBlock:^{
NSFetchRequest* req = [NSFetchRequest fetchRequestWithEntityName:@"EntityB"];
NSArray* results = [mainContext executeFetchRequest:req error:&error];
}];
//fetch EntityA (asyn on main context)
[mainContext performBlock:^{
NSFetchRequest* req = [NSFetchRequest fetchRequestWithEntityName:@"EntityA"];
req.fetchBatchSize = 20; //(1)
req.predicate = [NSPredicate predicateWithFormat:@"foo = %@", @"wwww"]; //(2)
NSArray* results = [mainContext executeFetchRequest:req error:&error]; //(3)
}];
このコードをcom.apple.CoreData.ConcurrencyDebug
オプション付きで実行すると、//(3)
の行でなぜか違反を検知して、例外が発生してしまいます。
やや複雑ですがやっていることは、
- RootSaving Context ( private queue)
- Main Context ( main queue )
- Private Context (private queue)
このようなコンテキストの親子関係があり、各context上でEntityA
、EntityB
をinsertしたりfetchしたりしているだけです。
さらに謎なのが
req.fetchBatchSize = 20; //(1)
req.predicate = [NSPredicate predicateWithFormat:@"foo = %@", @"wwww"]; //(2)
このどちらかの行をコメントアウトすれば違反は発生しなくなります。あきらかにcom.apple.CoreData.ConcurrencyDebug
オプションのバグっぽいですよね。
この件については現在Appleに問い合わせ中です。
https://openradar.appspot.com/radar?id=4944086339420160
ちなみに、iOS8.4の時代にも同様の報告があったようです。この不具合?が現在もまだ続いているのでしょうか・・。
http://stackoverflow.com/questions/29741359/coredata-multithreading-violation-debugging
プログラミングミスを防止するためにも有用なオプションなので、ぜひ正しく動くようになってほしいものです。