Core Data concurrency with NSPersistentContainer

2020-03-07 07:54发布

NOTE: I've looked at similar questons, but didn't find one that describes this situation.

I'm looking at the following example code from Apple regarding Core Data concurrency (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html)

NSArray *jsonArray = …;
NSPersistentContainer *container = self.persistentContainer;
[container performBackgroundTask:^(NSManagedObjectContext *context) {
    for (NSDictionary *jsonObject in jsonArray) {
        AAAEmployeeMO *mo = [[AAAEmployeeMO alloc] initWithContext:context];
        [mo populateFromJSON:jsonObject];
    }
    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Failure to save context: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }
}];

In my app, the save is not initiated until the user taps the save button on the screen. How do I go about it, should I use a child context instead for that situation, where the private context is a property of the VC?

NSArray *jsonArray = …; //JSON data to be imported into Core Data
NSManagedObjectContext *moc = self.persistentContainer.viewContext; //Our primary context on the main queue

NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:moc];

[private performBlock:^{
    for (NSDictionary *jsonObject in jsonArray) {
        NSManagedObject *mo = …; // WHICH CONTEXT TO USE?  <<<======
        //update MO with data from the dictionary
    }
    NSError *error = nil;
    if (![private save:&error]) {
        NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }
}

And then once the user taps save do this:

NSManagedObjectContext *moc = self.persistentContainer.viewContext; //Our primary context on the main queue
   [moc performBlockAndWait:^{
            NSError *error = nil;
            if (![moc save:&error]) {
                NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
                abort();
            }
        }];
    }];

Also note the question which moc to use in the example above (<<<=====)

EDIT: What I ended up doing in the end is save the child context immediately so that the table only uses viewContext to display the results. If the user then exits without saving, I delete all the results again from the viewContext. The save button is still there, but now only sets a flag indicating not to delete the results.

1条回答
走好不送
2楼-- · 2020-03-07 08:46

If you have one page of forms and you want it to save when the user presses the save button, then simply take the data from the textFields (or whatever your data input is) and put it into core data using performBackgroundTask. Since the data is only stored in the textFields while the user is editing if the user pushes back his edits will be lost.

If you have a lots of changes to a complex document with lots of different entities that the user can create or destroy or link and all of that is only saved when the user presses save then you should use a child context. You would display the data based on the values in the child context but only push those changes to the parent context if the user presses save. This is a very rare situation and I have never personally encountered the need to do this.

I strongly suspect that you are in the first case. Don't use child context. Use performBackgroundTask and save the data when the user presses save.

(also the correct context to use inside the block [private performBlock:^{ is the private context)

查看更多
登录 后发表回答