Observación de cambios en una matriz mutable utilizando KVO frente a NSNotificationCenter

En mi modelo tengo una serie de objetos llamados eventos. Me gustaría que mi controlador sea notificado cada vez que se agregue un nuevo objeto a los eventos.

Pensé que una buena manera de hacer esto sería usar el patrón KVO para recibir una notificación cuando cambien los eventos (a partir de la adición de un nuevo objeto)

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

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

Pero el observeValueForKeyPath no se estaba llamando al método y descubrí que las matrices no son compatibles con KVO :-(

Una opción es activar manualmente el método llamando cambiará el valor de la clave para la ruta clave

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

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

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 crear una propiedad separada que realice un seguimiento de cuántos elementos hay en el matriz mutable (me gustaría que pudieras observar @"events.count" ).

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

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

// Delegate
[myController eventsDidChange];

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

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

preguntado el 03 de mayo de 12 a las 18:05

2 Respuestas

No debe crear propiedades públicas directas para colecciones mutables para evitar que muten sin su conocimiento. NSArray no es el valor clave observable en sí mismo, sino su uno a muchos perfecta @"events" es. He aquí cómo observarlo:

Primero, declara una propiedad pública para una colección inmutable:

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

Luego, en su implementación, respalde con un ivar mutable:

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

y anular el getter 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, pueden obtener un objeto proxy mutable llamando -[Model mutableArrayValueForKey:@"events"].

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

Esto activará 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 del accesores de matriz.

Ver también: Observación de un NSMutableArray para su inserción / extracción.

contestado el 23 de mayo de 17 a las 12:05

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

Ciertamente me estoy perdiendo algo aquí. ¿Podría alguien explicar dónde se llamaría a setEvents si alguien agregara o insertaraObject: atIndex: un objeto en la matriz mutable de respaldo? - Alex Zavatone

Por el documentos sobre métodos de acceso, debe implementar:

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

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

Luego, KVO activará las notificaciones cuando se llamen.

respondido 20 mar '15, 23:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.