Jiho Kang
Jiho Kang

Reputation: 2492

Managed Object Context not saving to persistant store

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

Answers (2)

Lorenzo B
Lorenzo B

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

bandejapaisa
bandejapaisa

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

Related Questions