ios swift core-data concurrency

ios - Simultaneidad NSPersistentContainer para guardar en datos centrales



swift core-data (1)

He leído algunos blogs sobre esto, pero todavía estoy confundido sobre cómo usar NSPersistentContainer performBackgroundTask para crear una entidad y guardarla. Después de crear una instancia llamando al método de conveniencia init(context moc: NSManagedObjectContext) en el performBackgroundTask() { (moc) in } si performBackgroundTask() { (moc) in } container.viewContext.hasChanges esto devuelve falso y dice que no hay nada que guardar, si llamo guardar en moc (MOC de fondo creado para este bloque) Recibo errores como este:

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; ...}" )}

Así que no pude lograr que la concurrencia funcionara y realmente agradecería que alguien me explicara la forma correcta de usar esta función en los datos principales en iOS 10


TL: DR : su problema es que está escribiendo utilizando viewContext y con contextos de fondo. Solo debe escribir en core-data de una manera sincrónica.

Explicación completa: si un objeto se cambia al mismo tiempo desde dos contextos diferentes, los datos centrales no saben qué hacer. Puede configurar mergePolicy para establecer qué cambio debería ganar, pero eso realmente no es una buena solución, porque puede perder datos de esa manera. La forma en que muchos profesionales han estado lidiando con el problema durante mucho tiempo fue tener una cola de operaciones para poner en cola las escrituras para que solo haya una escritura en curso a la vez, y tener otro contexto en el hilo principal solo para lecturas . De esta manera, nunca obtienes ningún conflicto de fusión. (Consulte https://vimeo.com/89370886 para obtener una excelente explicación sobre esta configuración).

Hacer esta configuración con NSPersistentContainer es muy fácil. En su administrador de datos centrales, cree una NSOperationQueue

//obj-c _persistentContainerQueue = [[NSOperationQueue alloc] init]; _persistentContainerQueue.maxConcurrentOperationCount = 1; //swift let persistentContainerQueue = OperationQueue() persistentContainerQueue.maxConcurrentOperationCount = 1

Y escribe todo usando esta cola:

// 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 } } }

Cuando llama a enqueueCoreDataBlock el bloque se enqueueCoreDataBlock para garantizar que no haya conflictos de fusión. Pero si escribe en viewContext eso anularía esta configuración. Del mismo modo, debe tratar cualquier otro contexto que cree (con newBackgroundContext o performBackgroundTask ) como de solo lectura porque también estarán fuera de la cola de escritura.

Al principio pensé que la NSPersistentContainer de performBackgroundTask tenía una cola interna, y las pruebas iniciales lo respaldaron. Después de más pruebas, vi que también podría conducir a la fusión de conflictos.