Parsing in an NSManagedObjectContext background thread and issues with “different contexts”
-
16-06-2021 - |
Question
I am trying to parse a XML document and store its data in a Core Data store using an background queue. I am using the new nested UIManagedObjectContext concept introduced in iOS 5.
I have added a new category to my NSManagedObject company, which handles the parsing of company specific data:
- (void)parseAttributesFrom:(NSString*)xmlStr
inManagedObjectContext:(NSManagedObjectContext*)managedObjectContext
{
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = self.managedObjectContext;
[context performBlock:^{
IBCompany *company = self;
[company setValue:[[[document.root childNamed:@"CoIDs"] childWithAttribute:@"Type" value:@"CompanyName"] value] forKey:@"companyName"];
...
This is how I call this method: [company parseAttributesFrom:xmlStr inManagedObjectContext:self.managedDocument.managedObjectContext];
Please note that my issue is unchanged, even if I pass the managed object context as a parameter to the method.
When I run the code, it crashes where I set the annualPeriod to the company with the error message NSInvalidArgumentException: 'Illegal attempt to establish a relationship 'company' between objects in different contexts (source = <IBEstPeriod: ...
:
IBEstPeriod *annualPeriod = [NSEntityDescription insertNewObjectForEntityForName:@"IBEstPeriod" inManagedObjectContext:context];
[annualPeriod setCompany:company];
I am struggling to understand why the error should be related to different contexts when the code runs in the same background ideas. I would appreciate any help!!
Solution
I think, that reason is that IBCompany *company = self;
is in the context
you pass, and then you set the new connection in another context.
That's the reason.
OTHER TIPS
You may not do this:
context.parentContext = self.managedObjectContext;
if you are using UIManagedDocument as in here:
[company parseAttributesFrom:xmlStr inManagedObjectContext:self.managedDocument.managedObjectContext];
That is because UIManagedDocument already uses nested contexts and self.managedDocument.managedObjectContext has its parent context set. Now you are swapping it out and by doing that you break things.
Here's a quote from UIManagedDocument Class Reference.
To support asynchronous data writing, Core Data actually uses a pair of nested managed object contexts. The parent context is created on a private thread, the child context is created on the main thread. You get the child context from the managedObjectContext property.