I've recently begun experimenting with Core Data's newer initWithConcurrencyType:NSPrivateQueueConcurrencyType construction of a Managed Object Context. When using this type of MOC, we should use performBlock: or performBlockAndWait: to "ensure the block operations are executed on the queue specified for the context."
As part of this I moved the Managed Object creation calls of initWithEntity:insertIntoManagedObjectContext: inside the performBlock: block, to be run on the MOC's private queue.
This matches the strategy shown in Apple's Core Data Concurrency article:
[private performBlock:^{
for (NSDictionary *jsonObject in jsonArray) {
NSManagedObject *mo = ... ; //Managed object that matches the incoming JSON structure
}
NSError *error = nil;
if (![private save:&error]) {
NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
}
}];
I've found myself initializing managed objects in void methods that actually return the newly created object in a completion block.
+ (void)managedObjectFromJSON:(NSDictionary *)json completion:(void (^)(XYZManagedObject *object))completion;
This architecture introduces complexity compared to simply returning the objects from the JSON parsing method. I also seem to be hitting concurrency issues/crashes related to this design, perhaps in the async load of relationship Managed Objects chained together in these completion block constructors.
Each of my Managed Objects doing something like the below, returning the created object via a completion block called from within the performBlock: block.
[managedObjectContextPrivateQueue performBlock:^{
NSEntityDescription *entity = [NSEntityDescription entityForName:@"entity" inManagedObjectContext:managedObjectContextPrivateQueue];
XYZManagedObject *managedObject = [[XYZManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:managedObjectContextPrivateQueue];
[managedObjectContextPrivateQueue save:&error]
completion(managedObject);
}];
Or, can the Managed Object be created outside the performBlock: block, and only insert/save operations moved inside? Note that the NSEntityDescription method does need to access the MOC outside the block.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"entity" inManagedObjectContext:managedObjectContextPrivateQueue];
XYZManagedObject *managedObject = [[XYZManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
[managedObjectContextPrivateQueue performBlock:^{
[managedObjectContextPrivateQueue insertObject:managedObject];
[managedObjectContextPrivateQueue save:&error]
}];
return managedObject;
Or, perhaps I should use performBlockAndWait: to wait for the block to run, then return the Managed Object from the method directly rather than from a completion block.
How should Managed Objects be created and returned when Managed Object Context call should be routed through the performBlock: private queue?