CoreData Nested Contexts: What is the Right Way to Save Context?

I use a nested context template to support multi-threaded work with CoreData. I have a singleton class CoredDataManager, and contexts:

self.masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; self.masterContext.persistentStoreCoordinator = self.persistentStoreCoordinator; self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; self.mainContext.parentContext = self.masterContext; 

For each insert operation in response from a web service, I use the API of my CoreDataManager to get a new managed context:

 - (NSManagedObjectContext *)newManagedObjectContext { NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; workerContext.parentContext = self.mainContext; return workerContext; } 

It looks something like this (the PlayerView class is a subclass of the NSManagedObject class):

 [PlayerView insertIfNeededByUniqueKey:@"playerViewId" value:playerViewId inBackgroundWithCompletionBlock:^(NSManagedObjectContext *context, PlayerView *playerView) { playerView.playerViewId = playerViewId; playerView.username = playerViewDictionary[@"name"]; [context saveContextWithCompletionBlock:^{ //do something } onMainThread:NO];//block invocation on background thread }]; 

The saveContextWithCompletionBlock method is implemented in the NSManagedObjectContext category:

 - (void)saveContextWithCompletionBlock:(SaveContextBlock)completionBlock onMainThread:(BOOL)onMainThread { __block NSError *error = nil; if (self.hasChanges) { [self performBlock:^{ [self save:&error]; if (error) { @throw [NSException exceptionWithName:NSUndefinedKeyException reason:[NSString stringWithFormat:@"Context saving error: %@\n%@\n%@", error.domain, error.description, error.userInfo] userInfo:error.userInfo]; } if (completionBlock) { if (onMainThread && [NSThread isMainThread]) { completionBlock(); } else if (onMainThread) { dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(); }); } else if ([NSThread isMainThread]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{ completionBlock(); }); } else { completionBlock(); } } }]; } } 

Then at some point I call the CoreDataManager method to save the main context:

 - (void)saveMasterContext; { __block NSError *error; [self.mainContext performBlock:^{ [self.mainContext save:&error]; [self treatError:error]; [self.masterContext performBlock:^{ [self.masterContext save:&error]; [self treatError:error]; }]; }]; } 

I have two main classes: subclasses of NSManagedObject - PlayerView and Post. PlayerView is related to one to many to Post. PlayerView is saved and everything is in order. The message is never saved, and I get an error message:

CoreData: error: Mutation of the managed object 0x17dadd80 (0x17daf930) after removing it from the context.

I think the problem in context preserves logic.

+6
source share
2 answers

First of all, the error that you encounter usually occurs when the context in which you created the new managed object leaves (is freed) before you have the opportunity to save it.

Secondly, the best way to ensure that the context is stored in the appropriate stream is to use performBlock or performBlockAndWait instead of figuring out which stream the context belongs to. Here is an β€œsave” example that saves the context safely:

 + (BOOL)save:(NSManagedObjectContext *)context { __block BOOL saved = NO; [context performBlockAndWait: { NSError *error; saved = [context save:&error]; if (!saved) { NSLog("failed to save: %@", error); } }] return saved; } 

Regarding the use of nested private contexts (with the main context flow as the parent), our team ran into some problems with this model (I don’t remember exactly what it was), but we decided to listen to NSManagedObjectContextDidSaveNotification and use mergeChangesFromContextDidSaveNotification to update the contexts.

Hope this helps.

0
source

An excellent tutorial from Bart Jacobs: Basic data from Scratch: Concurrency describes two approaches in detail, a more elegant solution includes a parent / child managed object, including the proper preservation of the context.

0
source

All Articles