ここで説明したのと同じ問題を実験しました。問題は、DEFAULT コンテキストの子 MOC を使用してコアデータに保存すると、90% の確率で、メイン スレッド コンテキストの結果が子 MOC からの変更で更新されますが、そうでない場合もあります。
私のテストでは、同じエンティティを繰り返し (あまり速くなく、UI を使用して) 書き込みました。Ad エンティティ (favorite_count) と、Ad とユーザーの間のお気に入りの関係です。
DEFAULT Context の子 MOC から保存すると、これは MagicalRecord の予期される出力です (updatedObjects カウントも出力しました)。
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke_2]↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ POST FAVORITE ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
2013-09-19 11:40:35.154 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb8b55e0) → Saving <NSManagedObjectContext (0xb8b55e0): *** UNNAMED ***> on *** BACKGROUND THREAD ***
2013-09-19 11:40:35.155 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb8b55e0) → Save Parents? 1
2013-09-19 11:40:35.155 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb8b55e0) → Save Synchronously? 1
2013-09-19 11:40:35.155 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb8b55e0) → Updated objects = 4
2013-09-19 11:40:35.183 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Saving <NSManagedObjectContext (0xba72700): *** DEFAULT ***> on *** BACKGROUND THREAD ***
2013-09-19 11:40:35.184 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Save Parents? 1
2013-09-19 11:40:35.184 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Save Synchronously? 1
2013-09-19 11:40:35.185 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Updated objects = 2
2013-09-19 11:40:35.188 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Saving <NSManagedObjectContext (0xa3f7740): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
2013-09-19 11:40:35.189 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Save Parents? 1
2013-09-19 11:40:35.189 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Save Synchronously? 1
2013-09-19 11:40:35.189 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Updated objects = 2
2013-09-19 11:40:35.191 __70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke29(0xa3f7740) → Finished saving: <NSManagedObjectContext (0xa3f7740): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke672] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
**注: 子 MOC は 4 つの更新を登録しますが、この場合、同じ情報で更新された 2 つのオブジェクト (画像メタデータ) があります。
しかし、時にはこれが得られた出力です (親に updatedObjects はありません):
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke_2]↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ POST FAVORITE ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
2013-09-19 11:37:39.718 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb876d20) → Saving <NSManagedObjectContext (0xb876d20): *** UNNAMED ***> on *** BACKGROUND THREAD ***
2013-09-19 11:37:39.718 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb876d20) → Save Parents? 1
2013-09-19 11:37:39.719 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb876d20) → Save Synchronously? 1
2013-09-19 11:37:39.719 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb876d20) → Updated objects = 4
2013-09-19 11:37:39.720 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Saving <NSManagedObjectContext (0xba72700): *** DEFAULT ***> on *** BACKGROUND THREAD ***
2013-09-19 11:37:39.720 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Save Parents? 1
2013-09-19 11:37:39.720 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Save Synchronously? 1
2013-09-19 11:37:39.721 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xba72700) → Updated objects = 0
2013-09-19 11:37:39.722 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Saving <NSManagedObjectContext (0xa3f7740): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
2013-09-19 11:37:39.723 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Save Parents? 1
2013-09-19 11:37:39.723 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Save Synchronously? 1
2013-09-19 11:37:39.723 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3f7740) → Updated objects = 0
2013-09-19 11:37:39.725 __70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke29(0xa3f7740) → Finished saving: <NSManagedObjectContext (0xa3f7740): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke672] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
シリアル キューを使用して、CoreData に対するすべての書き込み操作を実行します。このシリアル キューは、現在のスレッドの子 MOC を作成します。これはライターのコードです。
+ (void) saveWithBlock: (void(^)(NSManagedObjectContext *threadContext))block
{
[ZCMagicalRecord saveWithBlock:block completion:nil];
}
+ (void) saveWithBlock: (void(^)(NSManagedObjectContext *threadContext))block completion:(MRSaveCompletionHandler)completion;
{
dispatch_async( _queue, ^{
NSManagedObjectContext *ctx = [NSManagedObjectContext MR_contextForCurrentThread];
[ctx performBlockAndWait:^{
block( ctx );
[ctx MR_saveWithOptions:MRSaveParentContexts|MRSaveSynchronously completion:completion];
}];
});
}
一方、
[MagicalRecord saveUsingCurrentThreadContextWithBlock:]
using UI MainThread を使用すると、変更は常に ROOT コンテキストと永続ストアに取り込まれます。
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke_2]↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ POST FAVORITE ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
2013-09-19 11:47:01.082 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb348550) → Saving <NSManagedObjectContext (0xb348550): *** DEFAULT ***> on *** MAIN THREAD ***
2013-09-19 11:47:01.082 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb348550) → Save Parents? 1
2013-09-19 11:47:01.083 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb348550) → Save Synchronously? 0
2013-09-19 11:47:01.083 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xb348550) → Updated objects = 5
2013-09-19 11:47:01.102 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3cd840) → Saving <NSManagedObjectContext (0xa3cd840): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
2013-09-19 11:47:01.102 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3cd840) → Save Parents? 1
2013-09-19 11:47:01.103 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3cd840) → Save Synchronously? 0
2013-09-19 11:47:01.103 -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xa3cd840) → Updated objects = 2
2013-09-19 11:47:01.106 __70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke29(0xa3cd840) → Finished saving: <NSManagedObjectContext (0xa3cd840): *** BACKGROUND SAVING (ROOT) ***> on *** BACKGROUND THREAD ***
[__52-[ZCServer postFavoriteAd:withDelegate:andSelector:]_block_invoke676] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
のコード[MagicalRecord saveUsingCurrentThreadContextWithBlock:]
は、私が書いたものと似ています (すべての書き込みを順番に行うために書いただけです):
+ (void) saveUsingCurrentThreadContextWithBlockAndWait:(void (^)(NSManagedObjectContext *localContext))block;
{
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
[localContext performBlockAndWait:^{
if (block) {
block(localContext);
}
[localContext MR_saveWithOptions:MRSaveParentContexts|MRSaveSynchronously completion:nil];
}];
}
このテストを行っている間、CoreData には別の種類の書き込みはありませんでした。ここに POST FAVORITE の例を投稿しましたが、投稿と投稿の間に DELETE FAVORITE が行われ、投稿と削除でランダムにエラーが発生します。サーバーの応答も正しく検証されています。
ノート
コード ブロックに渡されたオブジェクト コンテキストは無視されます。私は常に[EntityClass MR_CreateEntity]
実際のスレッドコンテキストを使用します。allwaysで使用されるMR_saveWithOptions:completion:
コンテキストは、ブロックを実行するスレッド コンテキストです。したがって、すべての操作で同じコンテキストが使用されます。このアプリは非常に複雑で、ほとんどの場合問題なく動作しますが、このエラーは、今のところ目に見えない問題を引き起こす可能性があります。