Cómo configurar automáticamente la relación de Core Data al usar contexts nesteds

Estoy luchando para encontrar una solución decente a un problema que surge cuando se usan contexts de objects gestionados nesteds en los datos básicos. Tome un model que tenga dos enites, Persona y Nombre, donde cada Persona tiene una relación de uno a uno con un Nombre, y la relación de la persona de Nombre no es opcional. Anteriormente, en el método -awakeFromInsert de -awakeFromInsert , crearía automáticamente una entidad Name para la nueva Persona:

 - (void)awakeFromInsert { [super awakeFromInsert]; NSManagedObjectContext *context = [self managedObjectContext]; self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context]; } 

Esto funciona bien en un solo context de object gestionado no nested. Sin embargo, si el context tiene un context principal, cuando se guarda el context secundario, se crea un nuevo object Person en el context principal y se -awakeFromInsert nuevamente -awakeFromInsert en este nuevo object antes de que se copien las properties y relaciones originales de la Persona. Entonces, se crea otro object de Nombre, luego "desconectado" cuando se copy la relación de nombre existente. El guardado falla porque falla la validation de la relación de person ahora nula del nombre flotante. Este problema se describe aquí , así como en otros lugares.

Hasta ahora, no he podido encontrar una buena solución a este problema. La creación lenta de la relación en el método getter realmente causa el mismo problema, porque el getter es invocado por la maquinaria interna de Core Data cuando se crea la nueva Persona en el context padre.

Lo único que puedo hacer es renunciar a la generación automática de relaciones y crear siempre la relación explícitamente en la class de controller que crea la Persona o en un método de conveniencia (por ejemplo, +[Person insertNewPersonInManagedObjectContext:] ) que solo es +[Person insertNewPersonInManagedObjectContext:] por mi código, y es siempre el método utilizado para crear un nuevo object Person explícitamente. Tal vez esta sea la mejor solución, pero preferiría no tener que ser tan estricta solo para permitir que se use un solo método para crear objects gestionados, cuando otros methods de creación que no tengo control sobre y cuyo uso no puedo usar comprobar fácilmente para / excluir, existir. Por un lado, significará varias subclasss de NSArrayController para personalizar la forma en que crean los objects administrados.

¿Alguien más que ha encontrado este problema presenta una solución elegante que permite que un object NSManagedObject cree un object de relación automáticamente al momento de la creación / inserción?

El primer pensamiento que viene a la mente es que mientras la relación de la person Name no es opcional, usted no dijo que la relación de name Person tampoco es opcional. ¿Está bien crear una Person sin Name , disponer con su código y luego crear el Name más adelante cuando realmente lo necesite?

De lo contrario, una forma simple es comprobar si está en el context raíz antes de crear un Name :

 - (void)awakeFromInsert { [super awakeFromInsert]; NSManagedObjectContext *context = [self managedObjectContext]; if ([context parentContext] != nil) { self.name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context]; } } 

Pero eso solo funciona si siempre creas nuevas instancias en un context secundario y nunca anidas contexts de más de un nivel de profundidad.

Lo que probablemente haría en su lugar es crear un método como el insertNewPersonInManagedObjectContext: que describas. A continuación, complételo con algo como lo siguiente para manejar los casos en los que se crean instancias para usted (por ejemplo, controlleres de matriz):

 - (void)willSave { if ([self name] == nil) { NSManagedObjectContext *context = [self managedObjectContext]; Name *name = [NSEntityDescription insertNewObjectForEntityForName:@"Name" inManagedObjectContext:context]; [self setName:name]; } } 

… y, por supuesto, no te molestes con un awakeFromInsert personalizado de awakeFromInsert

Terminé con la solución del método de conveniencia. Todas las subclasss NSManagedObject en mi aplicación tienen un método +insertInManagedObjectContext: Crear instancias de esos objects (en mi propio código) siempre se realiza utilizando ese método. Dentro de ese método, hago esto:

 + (instancetype)insertInManagedObjectContext:(NSManagedObjectContext *)moc { MyManagedObject *result = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntityName" inManagedObjectContext:moc] [result awakeFromCreation]; return result; } - (void)awakeFromCreation { // Do here what used to be done in -awakeFromInsert. // Set up default relationships, etc. } 

En cuanto al problema de NSArrayController, resolverlo no es malo en absoluto. Simplemente creé una subclass de NSArrayController, -newObject , y utilicé esa subclass para todos los NSArrayControllers relevantes en mi aplicación:

 @implementation ORSManagedObjectsArrayController - (id)newObject { NSManagedObjectContext *moc = [self managedObjectContext]; NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName] inManagedObjectContext:moc]; if (!entity) return nil; Class class = NSClassFromString([entity managedObjectClassName]); return [class insertInManagedObjectContext:moc]; } @end