I experimented the same issue described here. The problem is that using a child MOC of DEFAULT context to save in coredata, the 90% of the times, the results in the main thread context are updated with changes coming from child MOC, but sometimes they are not.
In my tests I writting the same entities repeatedly (not too fast, using the UI): an Ad entity (the favorite_count) and the favorite relation between the Ad and User.
When saving from a child MOC of DEFAULT Context this is the expected output of MagicalRecord (I printed the updatedObjects count too):
[__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] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
**Note: the child MOC registers 4 updates but there are two objects (images metadata) updated with same information in this case.
But sometimes this is the output obtained (no updatedObjects on parents):
[__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] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
I use a serial queue to do ALL write operations on CoreData. This serial queue creates a child MOC for current thread. This is the code for the writer:
+ (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];
}];
});
}
In the other hand, when I use the
[MagicalRecord saveUsingCurrentThreadContextWithBlock:]
using UI MainThread, the changes are ALLWAYS populated to ROOT context and to persistent store:
[__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] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ------*------ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
The code for [MagicalRecord saveUsingCurrentThreadContextWithBlock:]
is similar of what I wrote (I only wrote it to make all writes sequentially):
+ (void) saveUsingCurrentThreadContextWithBlockAndWait:(void (^)(NSManagedObjectContext *localContext))block;
{
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
[localContext performBlockAndWait:^{
if (block) {
block(localContext);
}
[localContext MR_saveWithOptions:MRSaveParentContexts|MRSaveSynchronously completion:nil];
}];
}
While I was doing this test, there wasn't another kind of writes in CoreData. I posted here the POST FAVORITE example, but DELETE FAVORITE was done between posts and the error occurs randomly in posts and deletes. The response of the server has been verified correctly too.
NOTE
The object context passed to code blocks are ignored. I allways use [EntityClass MR_CreateEntity]
that uses the actual thread context. And the context used in MR_saveWithOptions:completion:
allways is the thread context that executes the block. So the same context is used in all operations. The app is very complex and works fine in most of cases, but this error could make more troubles than I can't see by now.