2010-06-04 25 views
65

Necesito escribir un método de configuración personalizado para un campo (lo llamaremos foo) en mi subclase de NSManagedObject. foo se define en el modelo de datos y Xcode ha autogenerado los campos @property y @dynamic en los archivos .h y .m respectivamente.Métodos de configuración personalizada en Core-Data

Si escribo mi organismo como esto:

- (void)setFoo: (NSObject *)inFoo { 
    [super setFoo: inFoo]; 
    [self updateStuff]; 
} 

cuando me siento un advertencia del compilador en la llamada a super.

Alternativamente, si hago esto:

- (void)setFoo: (NSObject *)inFoo { 
    [super setValue: inFoo forKey: inFoo]; 
    [self updateStuff]; 
} 

entonces terminan en un bucle infinito.

Entonces, ¿cuál es el enfoque correcto para escribir un setter personalizado para una subclase de NSManagedObject?

Respuesta

1

Aquí está la manera de Apple para anular las propiedades de NSManagedObject (sin romper MVA), en su.m:

@interface Transaction (DynamicAccessors) 
- (void)managedObjectOriginal_setDate:(NSDate *)date; 
@end 

@implementation Transaction 
@dynamic date; 

- (void)setDate:(NSDate *)date 
{ 
    // invoke the dynamic implementation of setDate (calls the willChange/didChange for you) 
    [self managedObjectOriginal_setDate:(NSString *)date; 

    // your custom code 
} 

managedObjectOriginal_propertyName es un built-in método mágico sólo hay que añadir la definición de. Como se ve en la parte inferior de esta página What's New in Core Data in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0

+0

Buen lugar @malhal: no estaba al tanto de este cambio en iOS 10. –

100

Según the documentation, sería:

- (void) setFoo:(NSObject *)inFoo { 
    [self willChangeValueForKey:@"foo"]; 
    [self setPrimitiveValue:inFoo forKey:@"foo"]; 
    [self didChangeValueForKey:@"foo"]; 
} 

Esto es, por supuesto, ignorando el hecho de que sólo quieren NSManagedObjectsNSNumbers, NSDates, NSDatas y NSStrings como atributos.

Sin embargo, este podría no ser el mejor enfoque. Ya que desea que ocurra algo cuando cambie el valor de su propiedad foo, ¿por qué no simplemente observarlo con Key Value Observing? En este caso, parece que "KVO es el camino a seguir".

+0

Gracias Dave. Disculpas, el campo en realidad se define como un 'NSNumber *' pero estaba tratando de generalizar el problema. Intenté lo que sugeriste anteriormente, pero recibo un aviso de compilador de que mi clase puede no responder a '-setPrimitivePositionX:'. ¿Algunas ideas? Buena idea re. KVO. ¿Dónde sería el mejor lugar para registrarse? En '- (void) awakeFromInsert'? Me gustaría cancelar el registro en '- (void) dealloc' ¿verdad? –

+0

OK, agregué una sección privada '@ interface' en el archivo .m y eso corrigió la advertencia, pero los códigos todavía no se comportan como se esperaba. Necesito depurar esto! –

+0

En una investigación posterior, el colocador recibe una llamada correctamente cuando establezco explícitamente el valor en el objeto, pero no se llama cuando uso NSUndoManager para revertir el cambio. En cuyo caso supongo que KVO es un mejor enfoque general. –

17

Creo que hay un pequeño error: uso

[self setPrimitiveValue:inFoo forKey:@"foo"]; 

en lugar de

[self setPrimitiveFoo:inFoo]; 

esto funciona para mí.

+0

Gracias Martin. Como dices, KVO es el camino a seguir (me estoy registrando en '- (void) awakeFromFetch' y anulando el registro en' - (void) dealloc' y ahora lo he implementado y funciona con deshacer. –

+7

no usar - (void) dealloc para anular el registro, anular el registro de las observaciones en - (void) willTurnIntoFault en su lugar. De lo contrario, recibirá notificaciones innecesarias cuando un objeto se convierte en un error. Los nuevos objetos insertados no reciben un mensaje - (void) awakeFromFetch use - (void) awakeFromInsert too. –

+1

@Andrew Ebling, por favor responda su propia pregunta e incluya el código fuente de su solución. (Siéntase libre de cambiar los nombres de variables, etc., pero por favor manténgalo bien). Estoy trabajando en haciendo esto exacto. Estoy averiguando leyendo el enlace en KVC, ¡pero ver tu solución sería muy útil! :) – ma11hew28

19

Así es como estoy haciendo KVO en el atributo id de Photo : NSManagedObject. Si la identificación de la foto cambia, luego descarga la nueva foto.

#pragma mark NSManagedObject 

- (void)awakeFromInsert { 
    [self observePhotoId]; 
} 

- (void)awakeFromFetch { 
    [self observePhotoId]; 
} 

- (void)observePhotoId { 
    [self addObserver:self forKeyPath:@"id" 
       options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:NULL]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change 
         context:(void *)context { 
    if ([keyPath isEqualToString:@"id"]) { 
     NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey]; 
     NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];   
     if (![newValue isEqualToString:oldValue]) { 
      [self handleIdChange]; 
     } 
    } 
} 

- (void)willTurnIntoFault { 
    [self removeObserver:self forKeyPath:@"id"]; 
} 

#pragma mark Photo 

- (void)handleIdChange { 
    // Implemented by subclasses, but defined here to hide warnings. 
    // [self download]; // example implementation 
} 
+0

Si un objeto se borra, el contexto se guarda (objeto act aliado desasignado), deshacer invocado, la observación faltará. En 10.6+ también puedes establecer la observación en awakeFromSnapshotEvents. Para compatibilidad con versiones anteriores, consulte https://github.com/mbrugger/CoreDataDependentProperties Resuelve exactamente todos estos problemas. –

+2

En los documentos de apple, debe llamar a super en "awakeFromFetch" y "awakeFromInsert" – Fervus

+0

La prueba [newValue isEqualToString: oldValue] es innecesaria ya que la notificación solo se activará si no son iguales. –

0

Así es como lo haces 1-n (y presumo n-m): relaciones

Asumamos el nombre de relación se llama "estudiantes" en un objeto llamado "Escuela".

Primero debe definir los métodos primitivos de acceso para NSMutableSet. Xcode no generará estos automáticamente para usted.

@interface School(PrimitiveAccessors) 
- (NSMutableSet *)primitiveStudents; 
@end 

A continuación puede definir su método de acceso. Aquí voy a anular al colocador.

- (void)addStudentsObject:(Student *)student 
{ 
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&student count:1]; 

    [self willChangeValueForKey:@"students" 
       withSetMutation:NSKeyValueUnionSetMutation 
       usingObjects:changedObjects]; 

    [[self primitiveStudents] addObject:value]; 

    [self didChangeValueForKey:@"students" 
      withSetMutation:NSKeyValueUnionSetMutation 
       usingObjects:changedObjects]; 
} 
Cuestiones relacionadas