Implementación correcta de parent / child NSManagedObjectContext

A veces, mi aplicación inserta objects en el context del object gestionado que no deben savese necesariamente. Por ejemplo, cuando lanzo un modal de 'agregar entidad', creo un object gestionado y lo asigno al modal. Si el usuario salva de ese modo, guardo el context. Si cancela, elimino el object y no es necesario save.

Ahora introduje una function de "import" que cambia a mi aplicación (utilizando un esquema de URL) y agrega una entidad. Debido a que uno de estos modos podría estar abierto, no es seguro save el context en este punto. El object transitorio creado para el modal se saveá, incluso si el usuario cancela, y no hay garantía de que la eliminación (de la operación de cancelación) se saveá más tarde, el usuario puede abandonar la aplicación.

Del mismo modo, no puedo simplemente save cada vez que abandone mi aplicación. Si el modal está abierto en ese punto, el object temporal se saveá incorrectamente.

Para abordar esto, estoy tratando de usar un context infantil, como se analiza aquí . Después de haber leído todo lo que pude encontrar en TAN, tengo algunas preguntas:

  1. ¿Qué tipo de concurrency debo usar para cada context? Recuerde que no estoy haciendo esto por los beneficios de performance / subprocesamiento. Sé que no puedo usar NSConfinementConcurrencyType para el context principal si quiere tener contexts secundarios, pero no estoy seguro de cuál de las otras dos opciones es la más adecuada. Para el context secundario, ¿es necesario que coincida? ¿O puedo usar el tipo de confinamiento aquí? He intentado una variedad de combinaciones y todo parece funcionar bien, pero me gustaría saber cuál es el adecuado para mis requisitos.

  2. (problema secundario) ¿Por qué puedo hacer que esto funcione si uso un iVar de class? Pensé que debería poder declarar el context temporal en el método en el que se creó, y luego referirme a él utilizando entity.managedObjectContext. ¿Pero parece ser nulo para cuando accedo a él? Esto se rectifica si en su lugar uso un iVar para mantener la reference.

  3. ¿Cuál es la forma correcta o propagar el cambio al context principal? He visto varios comentarios utilizando diferentes implementaciones bloqueadas en cada uno de los contexts. ¿Depende de mi tipo de concurrency? Mi versión actual es:

    //save the new entity in the temporary context NSError *error = nil; if (![myObject.managedObjectContext save:&error]) {NSLog(@"Error - unable to save new object in its (temporary) context");} //propogate the save to the main context [self.mainContext performBlock:^{ NSError *error2 = nil; if (![self.mainContext save:&error2]) {NSLog(@"Error - unable to merge new entity into main context");} }]; 
  4. Cuando mi usuario guarda, envía un post a su delegado (mi controller de vista principal). El delegado se pasa el object que se agregó y debe ubicar el mismo object en el context principal. Pero cuando lo busco en el context principal, no se encuentra. El context principal contiene la entidad, puedo registrar sus datos y confirmar que está allí, pero la dirección es diferente. Si esto debe ocurrir (¿por qué?), ¿Cómo puedo ubicar el object agregado en el context principal después del guardado?

Gracias por cualquier idea. Disculpa por una pregunta larga y de varias partes, pero pensé que era probable que alguien hubiera abordado todos estos problemas anteriormente.

El model MOC padre / hijo es una característica realmente poderosa de Core Data. Simplifica increíblemente el antiguo problema de concurrency con el que teníamos que lidiar. Sin embargo, como ya dijiste, la simultaneidad no es tu problema. Para responder a sus preguntas:

  1. Tradicionalmente, usa NSMainQueueConcurrencyType para NSManagedObjectContext asociado con el hilo principal y NSPrivateQueueConcurrencyType s para contexts secundarios. El context secundario no necesita coincidir con su principal. NSConfinementConcurrencyType es lo que todos los NSManagedObjectContext obtienen por defecto si no especifica un tipo. Básicamente es el tipo "Gestionaré mis propios hilos para Core Data".
  2. Sin ver su código, mi suposition sería el scope dentro del cual crea el context secundario y se limpia.
  3. Cuando se utiliza el patrón de context padre / hijo, debe utilizar los methods de bloque. El mayor beneficio de usar los methods de bloque es que el sistema operativo se encargará de enviar las llamadas de método a los hilos correctos. Puede usar performBlock para la ejecución asincrónica, o performBlockAndWait para la ejecución síncrona.

Usaría esto como:

 - (void)saveContexts { [childContext performBlock:^{ NSError *childError = nil; if ([childContext save:&childError]) { [parentContext performBlock:^{ NSError *parentError = nil; if (![parentContext save:&parentError]) { NSLog(@"Error saving parent"); } }]; } else { NSLog(@"Error saving child"); } }]; } 

Ahora, debe tener en count que los cambios realizados en el context secundario (por ejemplo, Entidades insertadas) no estarán disponibles para el context principal hasta que guarde. Al context secundario, el context principal es el almacenamiento persistente. Cuando guardas, pasas esos cambios hasta el padre, quien luego puede savelos en el almacén persistente real. Guarda los cambios propaguados en un nivel. Por otro lado, al ir a un context infantil, los datos bajarán a través de todos los niveles (a través del padre y en el niño)

  1. objectWithID usar alguna forma de objectWithID en managedObjectContext. Son la manera más segura (y realmente única) de pasar objects entre contexts. Como mencionó Tom Harrington en los comentarios, es posible que desee utilizar existingObjectWithID:error: aunque porque objectWithID: siempre devuelve un object, incluso si pasa una ID no válida (que puede provocar excepciones). Para más detalles: Link

He tenido problemas similares y aquí hay respuestas a algunas partes de sus preguntas: 1. Usted debe poder usar concurrency type NSPrivateQueueConcurrencyType o NSMainQueueConcurrencyType 2. Digamos que ha creado un context temporal tempContext con el context principal mainContext (esto supone el iOS5). En ese caso, puede mover su object gestionado de tempContext a mainContext by-

 object = (Object *)[mainContext objectWithID:object.objectID]; 

A continuación, puede save el mainContext en sí.

Tal vez tambien

 [childContext reset]; 

si desea restablecer el context temporal.

  1. Si usa el patrón padre / hijo, generalmente declara el context padre con NSMainQueueConcurrencyType y los contexts hijo con NSPrivateQueueConcurrencyType . NSConfinementConcurrencyType se utiliza para el patrón de subprocesamiento clásico.

  2. Si quieres mantener el context, de alguna manera necesitas una fuerte reference a él.

  3. Simplemente invoca el método save en el context secundario para insert los cambios en el context primario, si quieres mantener los datos, también debes save en el context primario. No necesitas hacer esto dentro de un bloque.

  4. Hay varios methods para get un object específico de un context. No puedo decir cuál funcionará en su caso, pruébelo:

    - objectRegistenetworkingForID:

    - objectWithID:

    - existingObjectWithID:error: