NSPersistentContainer concurrency to persist master data

I have read several blogs about this but I am still confused on how to use the NSPersistentContainer performBackgroundTask

to create an object and persist it. After instantiating by calling convenience method init(context moc: NSManagedObjectContext)

in performBackgroundTask() { (moc) in }

, if I check container.viewContext.hasChanges

, this returns false and says nothing to save, if I call save on moc

(background MOC created for this block) I get errors like this:

fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=(
    "NSMergeConflict (0x17466c500) for NSManagedObject (0x1702cd3c0) with objectID '0xd000000000100000 <x-coredata://3EE6E11B-1901-47B5-9931-3C95D6513974/Currency/p4>' with oldVersion = 1 and newVersion = 2 and old cached row = {id = 2; ... }fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=(
    "NSMergeConflict (0x170664b80) for NSManagedObject (0x1742cb980) with objectID '0xd000000000100000 <x-coredata://3EE6E11B-1901-47B5-9931-3C95D6513974/Currency/p4>' with oldVersion = 1 and newVersion = 2 and old cached row = {id = 2; ...} and new database row = {id = 2; ...}"
)}

      

So, I was unable to work with concurrency and it would be very helpful if someone could explain to me the correct way to use this function for core data in iOS 10

+8


source to share


1 answer


TL: DR : Your problem is that you are writing using both viewContext

and background contexts. You should only write to core-data in one synchronous way.

Full explanation: if an object changes at the same time from two different contexts, the kernel data doesn't know what to do. You can set mergePolicy to indicate which change should win, but this is not really a good solution because you can lose data this way. Many professionals have dealt with this problem for a long time to have a queue of operations to enqueue writes so that only one write happens at a time and a different context on the main thread is read-only, so you never get any merge conflicts. (see https://vimeo.com/89370886 for a great explanation of this setting).

NSPersistentContainer

this setting using is NSPersistentContainer

very easy. In your master data manager, create an NSOperationQueue

//obj-c
_persistentContainerQueue = [[NSOperationQueue alloc] init];
_persistentContainerQueue.maxConcurrentOperationCount = 1;

//swift
let persistentContainerQueue = OperationQueue()
persistentContainerQueue.maxConcurrentOperationCount = 1

      



And do all the writes using this queue:

// obj c
- (void)enqueueCoreDataBlock:(void (^)(NSManagedObjectContext* context))block{
  void (^blockCopy)(NSManagedObjectContext*) = [block copy];

  [self.persistentContainerQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{
    NSManagedObjectContext* context =  self.persistentContainer.newBackgroundContext;
    [context performBlockAndWait:^{
      blockCopy(context);
      [context save:NULL];  //Don't just pass NULL here, look at the error and log it to your analytics service
     }];
  }]];
}

 //swift
func enqueue(block: @escaping (_ context: NSManagedObjectContext) -> Void) {
  persistentContainerQueue.addOperation(){
    let context: NSManagedObjectContext = self.persistentContainer.newBackgroundContext()
      context.performAndWait{
        block(context)
        try? context.save() //Don't just use '?' here look at the error and log it to your analytics service
      }
    }
}

      

When you call the enqueueCoreDataBlock

block is queued to ensure there are no merge conflicts. But if you write in viewContext

to defeat this setting. Likewise, you should treat any other contexts you create (with newBackgroundContext

or performBackgroundTask

) read-only, as they will also be out of the write queue.

At first I thought it NSPersistentContainer

performBackgroundTask

had an internal queue and initial testing supported that. After some additional testing, I saw that this can also lead to merge conflicts.

+11


source







All Articles