Reputation: 2773
I'm developing an application where I need to both calculate things (multiple seconds operations) and write things (sync data with a server) on a background thread.
Because of this I use two NSManagedObjectContexts (MOC), a child and a parent, and these must always be in sync. To make sure they are in sync I always edit/add data to the child MOC so it gets pushed to the main MOC with the following pattern:
[childMOC performBlock:^{
MyObject *myObject = *create new object in childMOC*
[childMOC save:&error];
[mainMOC performBlock:^{
[mainMOC save:&error];
// Is this mandatory to make it work correctly?
// [childMOC performBlock:^{
// [childMOC refreshObject:myObject mergeChanges:NO];
// }];
}];
}];
After a while I seemed to have two versions of the same object in the background context, one with a temporary ID and one with a permanent ID. If I e.g. added child objects to the "real" one (by passing fresh permanent ID from main to child MOC) I didn't see these objects when I retrieved my object in the background MOC cause it is the old temporary one that is cached.
I've seen the pattern above been used a lot, but it seems strange that no one mentioned this temporary/permanent ID problem.
It doesn't feel right that it can be two versions of the same object within a context. If I pass an NSManagedObjectID to the child MOC and retrieve that, shouldn't the child MOC update my existing object instead of creating a new one and leave my old temporary as cached default?
Do I need to use the commented row on each place I create an object?
Or maybe it works with mergeChangesFromContextDidSaveNotification, will that give the same effect?
Upvotes: 4
Views: 1702
Reputation: 2773
My solution was to:
1) Use the background MOC as the parent MOC and the main MOC as a child. As a bonus I don't need to save the main MOC to get the permanent IDs.
[DC.backgroundMOC performBlock:^{
// Add, save and update managed objects
[DC saveContext]; // The changes is being pushed to the main context
}];
2) Use NSManagedObjectContextDidSaveNotification to keep the main MOC up to date (the main MOC is updating the UI)
- (void) backgroundMOCSaved:(NSNotification*)notification {
[mainMOC performBlock:^{
[mainMOC mergeChangesFromContextDidSaveNotification:notification];
}];
}
Upvotes: 3
Reputation: 860
I had this problem and the solution was making sure all operations on the parent MOC are done with performBlock:, even the initial setup:
parentManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[parentManagedObjectContext performBlock:^{
[parentManagedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[parentManagedObjectContext setPersistentStoreCoordinator:coordinator];
}];
Once I did this, my child MOCs started picking up changes.
Upvotes: 1