18

En mi modelo tengo una matriz de objetos llamados eventos. Me gustaría que mi controlador sea notificado siempre que se agregue un nuevo objeto a los eventos.Observación Cambios en una matriz mutable usando KVO contra NSNotificationCenter

pensé que una buena manera de hacer esto sería usar el patrón de MVA para ser notificado cuando se agregan (a partir de un nuevo objeto) los acontecimientos cambios

// AppDelegate 
// events is a NSMutableArray @property/@synthesize etc... 

[appDelagate addObserver:self 
       forKeyPath:@"events" 
        options:NSKeyValueObservingOptionNew 
        context:NULL]; 

Pero el método observeValueForKeyPath no era siendo llamado y he descubierto que las matrices no son compatibles con MVA :-(

Una opción es activar manualmente el método llamando willChangeValueForKey para la keyPath

// ViewController 
[self willChangeValueForKey:@"events"]; 
[self.events addObject:event]; 
[self didChangeValueForKey:@"events"]; 

Pero esto se siente pesado ya que probablemente debería también hacer un seguimiento del estado antes y después de mi serie de eventos para que se pueda acceder desde el método observeValueForKeyPath.

Un enfoque podría ser usar una matriz estándar (en lugar de mutable) y crear/establecer una nueva instancia de eventos cada vez que quiera agregar un nuevo objeto, o podría hacer una propiedad separada que haga un seguimiento de cuántos los elementos están en la matriz mutable (me gustaría poder observar @ "events.count").

Otra opción sería usar NSNotificationCenter. También leí algunas respuestas que sugieren el uso de bloques (pero no tengo idea de por dónde empezar con eso).

Finalmente, ¿podría mantener una instancia de mi controlador en mi delegado y solo enviar un mensaje relevante?

// Delegate 
[myController eventsDidChange]; 

¿Es extraño mantener una referencia a un controlador de un delegado?

Tengo dificultades para entender cómo elegir cuál es el mejor enfoque para usar, por lo que cualquier consejo sobre el rendimiento, la futura flexibilidad del código y las mejores prácticas es muy apreciado.

Respuesta

18

No debe hacer propiedades públicas directas para colecciones mutables para evitar que mutan sin su conocimiento. NSArray no es el valor clave observable en sí mismo, pero su propiedad de uno a muchos@"events" es. Así es como para observar que:

En primer lugar, declarar una propiedad pública para una colección inmutable:

@interface Model 
@property (nonatomic, copy) NSArray *events; 
@end 

Luego, en su aplicación posterior con un Ivar mutable:

@interface Model() 
{ 
    NSMutableArray *_events; 
} 
@end 

y anular el captador y setter:

@implementation Model 

@synthesize events = _events; 

- (NSArray *)events 
{ 
    return [_events copy]; 
} 

- (void)setEvents:(NSArray *)events 
{ 
    if ([_events isEqualToArray:events] == NO) 
    { 
     _events = [events mutableCopy]; 
    } 
} 

@end 

Si otros objetos necesitan agregar eventos a su modelo, th Pueden obtener un objeto proxy mutable llamando al -[Model mutableArrayValueForKey:@"events"].

NSMutableArray *events = [modelInstance mutableArrayValueForKey:@"events"]; 
[events addObject:newEvent]; 

Esto disparará las notificaciones de KVO configurando la propiedad con una nueva colección cada vez.Para un mejor rendimiento y un control más granular, implemente el resto de array accessors.

Véase también: Observing an NSMutableArray for insertion/removal.

+2

¡Gracias! mutableArrayValueForKey hace el truco. ¿Tiene algún consejo sobre cómo elegir qué patrón usar (KVO, NotificationCenter, delegar) cuando desea comunicarse entre el modelo y el controlador? – MathewS

+0

Ciertamente me falta algo aquí. ¿Podría alguien explicar por qué se llamaría setEvents si alguien añadiera o insertaraObject: atIndex: un objeto para la matriz mutable de respaldo? –

0

por la docs on accessor methods, se debe implementar:

- (void)addEventsObject:(Event*)e 
{ 
    [_events addObject:e]; 
} 

- (void)removeEventsObject:(Event*)e 
{ 
    [_events removeObject:e]; 
} 

Entonces MVA disparará las notificaciones cuando se les llama.

Cuestiones relacionadas