I have a multi-threaded application where I need to merge a private context to the main context which in turn is connected to the persistent storage controller.
I also have the need to create temporary objects that are NOT managed (until I later on decide to manage them).
First, I tried to create my temporary objects as follows;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:myMainQueueContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
After deciding to keep the object or not, I then simply;
[privateContext insertObject:user];
Before I made the application multi-threaded, this worked great, but now after having torn things apart slightly and added the multi-thread concurrency by child/parent contexts, the result is NOT as expected.
By looking at the context's "registeredObjects", I can see that my created, and now inserted, user is managed in the privateContext. After saving it, the mainContext changes accordingly and I can see that it hasChanges and that there are now one object in the registeredObjects.
But looking closer at THAT registeredObject in the mainContext, reveal that it's emptied. No contents. All attributes are nil or 0 depending on type. Hence, one would expect that this might be because of the objectId is not the same... but it is ;( It's the same object. But without contents.
I tried to get some input on this concern in a different post here, but without success.
Child context objects become empty after merge to parent/main context
Anyhow, I finally got things to work by changing how I create my objects;
User* user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:privateContext];
Suddenly my child objects are merged to the mainContext without loosing their contents, for reasons to me unknown, but unfortunately this has also lead to the fact that I cannot any longer create temporary unmanaged objects... ;( I read that Marcus Zarra backed my first approach when it comes to creating unmanaged objects, but that does not work with merging contexts in my multi-threaded app...
Looking forward to any thoughts and ideas -- am I the only one trying to create temporary objects in an async worker-thread, where I only want to manage/merge a subset of them up to the mainContext?
EDIT
Concrete code showing what's working, and more importantly what's NOT working;
//Creatre private context and lnk to main context..
NSManagedObjectContext* privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
//Link private context to main context...
privateManagedObjectContext.parentContext = self.modelManager.mainManagedObjectContext;
[privateManagedObjectContext performBlock:^()
{
//Create user
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.modelManager.mainManagedObjectContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
[user setGuid:@"123123"];
[user setFirstName:@"Markus"];
[user setLastName:@"Millfjord"];
[privateManagedObjectContext insertObject:user];
//Debug before we start to merge...
NSLog(@"Before private save; private context has changes: %d", [privateManagedObjectContext hasChanges]);
NSLog(@"Before private save; main context has changes: %d", [self.modelManager.mainManagedObjectContext hasChanges]);
for (NSManagedObject* object in [privateManagedObjectContext registeredObjects])
NSLog(@"Registered private context object; %@", object);
//Save private context!
NSError* error = nil;
if (![privateManagedObjectContext save:&error])
{
//Oppps
abort();
}
NSLog(@"After private save; private context has changes: %d", [privateManagedObjectContext hasChanges]);
NSLog(@"After private save; main context has changes: %d", [self.modelManager.mainManagedObjectContext hasChanges]);
for (NSManagedObject* object in [privateManagedObjectContext registeredObjects])
NSLog(@"Registered private context object; %@", object);
for (NSManagedObject* object in [self.modelManager.mainManagedObjectContext registeredObjects])
NSLog(@"Registered main context object; %@", object);
//Save main context!
[self.modelManager.mainManagedObjectContext performBlock:^()
{
//Save main context!
NSError* mainError = nil;
if (![self.modelManager.mainManagedObjectContext save:&mainError])
{
//Opps again
NSLog(@"WARN; Failed saving main context changes: %@", mainError.description);
abort();
}
}];
}];
The above does NOT work, since it create a temporary object and then insert it into context. However, this slight mod make things work, but prevent me from having temporary objects...;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.modelManager.mainManagedObjectContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:privateManagedObjectContext];
Hence, I'm wondering; what's the difference? There must be some difference, obviously, but I don't get it.