Reputation: 2492
I have a threaded operation that creates a new managed object, saves it to the persistant store, then passes the objectID of the new objected via NSNotification to the main thread for further processing
However, when I try to access the newly created managed object from the main thread all the values that I set on the background thread return as empty.
** background thread
// create a new managed object context for this thread
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
// create the object
MyObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:context];
[newManagedObject setAValue:@"A"];
[newManagedObject setBValue:@"B"];
[newManagedObject setCValue:@"C"];
// save it on the main thread
[context performSelectorOnMainThread:@selector(save:) withObject:nil waitUntilDone:NO];
// post notification to main thread, pass the objectID
NSMutableDictionary *userInfo = [NSDictionary dictionaryWithObject:[newManagedObject objectID] forKey:@"objectID"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"doneInsertingObject" object:userInfo];
[context release];
** main thread
...
// register notification for background thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSomethingWithObject:) name:@"doneInsertingObject" object:nil];
...
- (void)doSomethingWithObject:(NSNotification*)noif
{
if([NSThread isMainThread] == NO)
{
// run this on the main thread
[self performSelectorOnMainThread:_cmd withObject:noif waitUntilDone:NO];
return;
}
// get managed object from objectID
NSDictionary *userInfo = [noif userInfo];
MyObject *object = (MyObject*)[appDelegate.managedObjectContext objectWithID:[userInfo valueForKey:@"objectID"]];
[appDelegate.managedObjectContext refreshObject:object mergeChanges:YES];
// these should return 'A, B, C' but all three return 'nil'
NSLog(@"aValue: %@", object.aValue);
NSLog(@"bValue: %@", object.bValue);
NSLog(@"cValue: %@", object.cValue);
}
// merge background thread moc with main moc
- (void)mergeContextChanges:(NSNotification *)notification
{
if([NSThread isMainThread] == NO)
{
// run this on the main thread
[self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
return;
}
// fault all updated objects
NSSet *updated = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
for(NSManagedObject *thing in updated)
{
[[appDelegate.managedObjectContext objectWithID:[thing objectID]] willAccessValueForKey:nil];
}
// merge changes to the main managed object context
[appDelegate.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
// force processing of any pending changes
[appDelegate.managedObjectContext processPendingChanges];
}
I've tried changing merge policies and there was no difference.
I've tried adding logging to the context merge method and I have confirmed receiving a "inserted" notification from the background thread before the doSomethingWithObject: method on the main thread is called.
Why is my data not being updated to the persistant store?
Upvotes: 3
Views: 5492
Reputation: 33428
I can't see where you save the context for your background thread. If it's this line
// save it on the main thread
[context performSelectorOnMainThread:@selector(save:) withObject:nil waitUntilDone:NO];
I don't know if it is correct. You have save the context from the thread that has created it and not in the main thread.
[context save:&error];
For further info, I suggest you to read the articles by Marcus Zarra importing-and-displaying-large-data-sets-in-core-data. You can find the sample code at the end. In addition you can find further info in using-core-data-on-multiple-threads.
Hope it helps.
Upvotes: 3
Reputation: 26972
Your NSManagedObjectContext
has to be saved on the thread it was created on (as @Flex_Addicted pondered).
After it has saved on the background thread, a notification will be posted telling you to merge changes from the background context to the main context.
Apples documents read "Saving in a Background Thread is Error-prone" - this isn't related to using another NSManagedObjectContext
. They are saying that if you have 1 context, and you try to dispatch the save operation to the background - THAT is error prone. If you have multiple contexts, they should only be used within one thread at a time.
Upvotes: 1