cocoa - ¿Cómo copio o muevo un NSManagedObject de un contexto a otro?
cocoa-touch core-data (4)
Tengo lo que supongo que es una configuración bastante estándar, con un MOC scratchpad que nunca se guarda (que contiene un montón de objetos descargados de la web) y otro MOC permanente que persiste en los objetos. Cuando el usuario selecciona un objeto de scratchMOC para agregarlo a su biblioteca, quiero: 1) eliminar el objeto de scratchMOC e insertarlo en permanentMOC, o 2) copiar el objeto en permanentMOC. Las preguntas frecuentes de Core Data dicen que puedo copiar un objeto como este:
NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];
(En este caso, context2 sería permanentMOC). Sin embargo, cuando hago esto, el objeto copiado tiene una falla; los datos están inicialmente sin resolver. Cuando se resuelve, más tarde, todos los valores son nulos; ninguno de los datos (atributos o relaciones) del managedObject original en realidad se copian o se referencian. Por lo tanto, no veo ninguna diferencia entre usar este método objectWithID e insertar un objeto completamente nuevo en permanentMOC usando insertNewObjectForEntityForName :.
Me doy cuenta de que puedo crear un nuevo objeto en permanentMOC y copiar manualmente cada par clave-valor del objeto anterior, pero no estoy muy contento con esa solución. (Tengo varios objetos administrados diferentes para los que tengo este problema, por lo que no quiero tener que escribir y actualizar copy: métodos para todos a medida que sigo desarrollando.) ¿Hay alguna manera mejor?
En primer lugar, tener más de un NSManagedObjectContext
en un único subproceso no es una configuración estándar. El 99% del tiempo solo necesitas un contexto y eso resolverá esta situación por ti.
¿Por qué siente que necesita más de un NSManagedObjectContext
?
Actualizar
Ese es en realidad uno de los pocos casos de uso que he visto donde tiene sentido. Para hacer esto, debe hacer una copia recursiva del objeto de un contexto a otro. El flujo de trabajo sería el siguiente:
- Crear un nuevo objeto en contexto persistente
- obtener un diccionario de los atributos del objeto de origen (use
-dictionaryWithValuesForKeys
y-[NSEntityDescription attributesByName]
para hacer esto. - establece el diccionario de valores en el objeto de destino (usando
-setValuesForKeysWithDictionary
) - Si tiene relaciones, tendrá que hacer esta copia de forma recursiva y recorrer las relaciones ya sea codificadas (para evitar cierta lógica circular) o usando las
-[NSEntityDescription relationshipsByName]
Como mencionó otro, puede descargar el código de muestra de mi libro del Libro de datos básicos de los programadores pragmáticos y ver una solución a este problema. Por supuesto, en el libro lo analizo más en profundidad :)
La documentación es engañosa e incompleta. Los métodos objectID no copian objetos, simplemente garantizan que ha obtenido el objeto específico que deseaba.
El context2
en el ejemplo es, de hecho, el contexto fuente y no el destino. Obtienes un nil porque el contexto de destino no tiene ningún objeto con esa ID.
Copiar objetos gestionados es bastante complicado debido a la complejidad del gráfico de objetos y la forma en que el contexto gestiona el gráfico. Debe recrear el objeto copiado en detalle en el nuevo contexto.
Aquí hay un código de ejemplo que he recortado del código de ejemplo de los Datos principales del programador pragmático : la API de Apple para datos persistentes en Mac OS X. (Es posible que pueda descargar el código completo del proyecto sin comprar el libro en el sitio Pragmatic.) Debe proporcionarle una idea aproximada de cómo copiar un objeto entre contexto.
Puede crear un código base que copie los objetos, pero los detalles de las relaciones de cada gráfico de objetos generalmente significan que debe personalizar para cada modelo de datos.
Tuve el mismo problema y encontré este artículo sobre entidades desconectadas creadas que luego podrían agregarse al contexto: http://locassa.com/temporary-storage-in-apples-coredata/
La idea es que tenga un NSManagedObject porque va a almacenar objetos en la base de datos. Mi obstáculo fue que muchos de estos objetos se descargan a través de HTTP API y quiero descartar la mayoría de ellos al final de la sesión. Piense en un flujo de publicaciones de usuarios, y solo quiero guardar las que fueron preferidas o guardadas como un borrador.
Creo todas mis publicaciones usando
+ (id)newPost {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
return post;
}
y luego las publicaciones se insertan en el contexto del objeto gestionado local cuando son preferidas
+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
// Set the post''s isFavorite flag
post.isFavorite = [NSNumber numberWithBool:isFavorite];
// If the post is being favorited and not yet in the local database, add it
NSError *error;
if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
[self.managedObjectContext insertObject:post];
}
// Else if the post is being un-favorited and is in the local database, delete it
else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
[self.managedObjectContext deleteObject:post];
}
// If there was an error, output and return NO to indicate a failure
if (error) {
NSLog(@"error: %@", error);
return NO;
}
return YES;
}
Espero que ayude.
managedObject
asegurarse de guardar el contexto en el que managedObject
reside. Para buscar el mismo objeto en un contexto diferente, debe estar presente en el almacenamiento persistente.
De acuerdo con la documentación , objectWithID:
siempre devuelve un objeto. Entonces, el hecho de que la falla se resuelva en un objeto significa que todos los valores nil
implican que no está encontrando su objeto en la tienda persistente.