Pergunta

I have an app that contains two managed object contexts:

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSManagedObjectContext *backgroundContext;

One of my view controllers is a UITableView controller that responds to NSFetchedResultsControllerDelegate. When I fetch for objects (from a remote web service), then I would like to process the data on a background thread, merge the changes to my main managedObjectContext and also update the UI.

With my current setup I get the following exception when I try of fetch for new objects.

CoreData: error: Serious application error.  An exception was caught from the delegate of   NSFetchedResultsController during a call to -controllerDidChangeContent:.  *** -[__NSArrayM  insertObject:atIndex:]: object cannot be nil with userInfo (null)

This exception gets raised in:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView endUpdates];

}

My managed objects contexts is create in a singleton class called AppController. It currently looks like this:

- (id)init {    
    self = [super init];
    if (self) {
        sharedInstance = self;

        // Registers NSManagedObjectContextDidSaveNotification 
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];

    }

    return self;
}

- (void)mergeChanges:(NSNotification *)notification {

    //should tell the main thread moc to run on the main thread, and merge in the changes there
    [self.managedObjectContext  performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)  withObject:notification waitUntilDone:YES];

}


- (void)saveContext {    
    NSError *childError = nil;
    [self.backgroundContext save:&childError];

    [self.managedObjectContext performBlock:^{
        NSError *parentError = nil;
        [self.managedObjectContext save:&parentError];
    }];
}


// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext {
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
        self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
        self.backgroundContext.parentContext = self.managedObjectContext;
    }
    return _managedObjectContext;
}

Any ideas on what I'm missing or doing wrong?

Foi útil?

Solução

I think you MUST create your backgroundContext from within your backround process.

and you need to listen for the notification sent by the backgroundContext:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundContext];

in my case runupdate is run in the background process:

- (void) runUpdate {
    NSManagedObjectContext *managedContext = [[NSManagedObjectContext alloc] init];
    managedContext.persistentStoreCoordinator = ...;
    [updateController registerBackgroundMoc:managedContext];
    //do all update stuff
    NSError *error;
    if (![managedContext save: &error] {
       ...
    }
    [updateController unregisterBackgroundMoc:managedContext];
}

in my updateController I did the following:

- (void) registerBackgroundMoc: (NSManagedObjectContext *) updateMoc {
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(mergeChanges:) name: NSManagedObjectContextDidSaveNotification object: updateMoc];
}

- (void) unregisterBackgroundMoc: (NSManagedObjectContext *) updateMoc {
    [[NSNotificationCenter defaultCenter] removeObserver: self name: NSManagedObjectContextDidSaveNotification object: updateMoc];
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top