I have set up a nonhierarchical dual-MOC architecture (one for the main thread, one for a private thread) with save notifications for merging changes:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainContext setPersistentStoreCoordinator:coordinator];
[mainContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
_managedObjectContext = mainContext;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSaveMainQueueContext:)
name:NSManagedObjectContextDidSaveNotification
object:_managedObjectContext];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)privateManagedObjectContext
{
if (_privateManagedObjectContext != nil) {
return _privateManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateContext setPersistentStoreCoordinator:coordinator];
[privateContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
_privateManagedObjectContext = privateContext;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSavePrivateQueueContext:)
name:NSManagedObjectContextDidSaveNotification
object:_privateManagedObjectContext];
}
return _privateManagedObjectContext;
}
- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification
{
@synchronized(self) {
[self.managedObjectContext performBlock:^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
- (void)contextDidSaveMainQueueContext:(NSNotification *)notification
{
@synchronized(self) {
[self.privateManagedObjectContext performBlock:^{
[self.privateManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
Now, I've updated some object A on the main thread in the main MOC, and now I'm in some method and working within a block on my private MOC queue/thread:
[self.privateManagedObjectContext performBlockAndWait:^{
...
I save my main-thread MOC:
[self.managedObjectContext performBlockAndWait:^{
NSError *contextError;
if (![self.managedObjectContext save:&contextError]) NSLog(@"ERROR SAVING MOC : %@ ; %@", contextError, [contextError userInfo]);
}];
The save is successful (verified) and triggers the save notification (verified), which executes the merger.
But the MOCs remain inconsistent: when I then fetch object A from my main MOC and log the number of objects in its relationship R, I find that the number of objects differs from when I fetch object A from my private MOC and log the number of objects in relationship R.
A bit downstream of this, in a related chain of events, I go to save my private MOC. The app pauses (only if I have enabled an all-exceptions or all-objective-c-exceptions breakpoint), and execution can be resumed without any apparent harm. This issue is described here: core data MOC save pauses execution and fails to save (w/o error or crash). I have a feeling it's related, but I suspect this failure to properly merge is a more fundamental problem.
Other notes:
- I have tried every merge policy
- I found that if I attempt to execute the merger using performBlockAndWait, the app hangs indefinitely; I don't know whether that is the expected behavior.
- I've read every question I can find about this and tried everything I can think of.
Is there something wrong with this code? What else can I try? Thanks!
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…