Search code examples
objective-cioscore-datansmanagedobjectnsmanagedobjectcontext

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?


Solution

  • 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.