2012-04-07 8 views
7

Me gustaría recibir una notificación cuando el recuento, es decir. número de elementos en un NSArray cambia .. Por supuesto que no necesitaría esto, si tuviera el control de la adición y eliminación de objetos en la matriz. Pero no lo estoy, sucede de manera impredecible con respecto al Modelo de Procesos de Negocio y depende de factores externos. ¿Hay alguna solución simple y elegante?Cuenta de observación en NSMutableArray

EDIT: estoy corrigiendo esto a NSMutableArray por supuesto ..

+0

No estoy al 100% en esto, pero una clave de acceso a una matriz y el sufijo '@ count' es la forma de KVC para obtener este valor. ¿Entonces quizás puedas observar a KVO 'array @ count'? https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html#//apple_ref/doc/uid/20002176-BAJEAIEE – joerick

Respuesta

15

Tendrá que utilizar KVC. Pero, ¿cómo hacerlo? Después de todo, NSMutableArray no es compatible con Key-Value-Coding por sus métodos de mutación o cambios de contenido. La respuesta es proxying: como subclase NS [Mutable], la matriz es demasiado complicada.

NSProxy es una pequeña gran clase que puede usar para interceptar los mensajes enviados a su matriz como si fuera un NSMutableArray, y luego reenviarlos a alguna instancia interna. Desafortunadamente, tampoco cumple con KVC, ya que las entrañas de KVC viven en NSObject. Tendremos que usar eso, entonces. Una interfaz de muestra podría ser algo como esto:

@interface CFIKVCMutableArrayProxy : NSObject { 
    NSMutableArray *_innerArray; 
} 

- (NSUInteger)count; 

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index; 
- (void)removeObjectAtIndex:(NSUInteger)index; 
- (void)addObject:(id)anObject; 
- (void)removeLastObject; 
- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes; 
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject; 

//… 

@end 

Como se puede ver, estamos simulando una interfaz para NSMutableArray, que es necesaria, ya que nuestro proxy debería poner en práctica todo como si se tratara de un NSMutableArray. Esto también hace que la implementación sea lo más simple posible, ya que podemos reenviar los selectores a nuestro puntero interno NSMutableArray. En aras de la brevedad, sólo voy a implementar dos métodos para mostrar lo que se ve como un esquema general:

@implementation CFIKVCMutableArrayProxy 

//… 

- (NSUInteger)count { 
    return _innerArray.count; 
} 

- (void)addObject:(id)anObject { 
    [self willChangeValueForKey:@"count"]; 
    [_innerArray addObject:anObject]; 
    [self didChangeValueForKey:@"count"]; 
} 

- (void)removeLastObject { 
    [self willChangeValueForKey:@"count"]; 
    [_innerArray removeLastObject]; 
    [self didChangeValueForKey:@"count"]; 
} 

@end 

Si no tiene oportunidades para envolver una serie como esta, a continuación, tratar de volver a pensar en su código . Si una dependencia externa te está forzando a este tipo de esquina, intenta eliminarla. Siempre es malo trabajar con tus propias herramientas.

+0

Ha pasado algún tiempo y una mejora afortunada ser implementado por parte de un código que está fuera de mi control: el objeto fachada que contiene la matriz ahora emitirá NSNotifications cuando cambie el modelo. Su solución propuesta es digna de mención, sin embargo, se olvidó del pequeño pero importante hecho de que yo no tenía el control de la matriz. Por lo tanto, no me fue posible inyectar el objeto proxy en la fachada en lugar de la matriz. –

+0

Por necesidad aprendí algo nuevo. – naz

+0

@CodaFi Gracias por la explicación :) –

6

Para observar los cambios en una mutableArray necesita utilizar proxy de objeto mutable propuesta por

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key 

que es compatible KVO, es decir, cualquier cambio de objeto proxy envía voluntad/hizo cambiar notificaciones.

La siguiente clase de demostración muestra la aplicación plena

@interface DemoClass : NSObject 

@property (nonatomic) NSMutableArray *items; 

- (void)addItemsObserver:(id)object; 
- (void)removeItemsObserver:(id)object; 

@end 

@implementation DemoClass 

- (NSMutableArray *)items; 
{ 
    return [self mutableArrayValueForKey:@"_items"]; 
} 

- (void)addItemsObserver:(id)object 
{ 
    [self addObserver:object forKeyPath:@"[email protected]" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; 
} 

- (void)removeItemsObserver:(id)object 
{ 
    [self removeObserver:object forKeyPath:@"[email protected]" context:nil]; 
} 
@end 


@interface ObservingClass : NSObject 

@property (nonatomic) DemoClass *demoObject; 

@end 

@implementation ObservingClass 

- (instanstype)init 
{ 
    if (self = [super init]) { 
     _demoObject = [DemoClass new]; 

     [_demoObject addItemsObserver:self]; 
    } 
    return self; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath 
        ofObject:(id)object 
        change:(NSDictionary *)change 
        context:(void *)context 
{ 
    NSLog(@"is called on demoObject.items.count change"); 
} 

- (void)dealloc 
{ 
    [_demoObject removeItemsObserver:self]; 
} 

@end 

Ahora cada vez que se añade o elimina un objeto en el items verá nuevo registro en la consola (observeValueForKeyPath se llama).

Cualquier cambio directo de la matriz sintetizada automáticamente _items no tendrá ningún efecto.

También tenga en cuenta que necesita establecer el observador en [email protected] (observar [email protected] no tiene sentido).

Tenga en cuenta que no necesita iniciar _items o self.items. Se realizará detrás de la escena cuando llame al items getter.

Cada vez que cambie la "matriz" items obtendrá el nuevo objeto _items con una nueva dirección. Pero aún puedo encontrarlo a través del proxy items.

+0

Es posible que desee un método 'removeProxyItemsObserver:' también. –

+0

@AaronBrager, que se acaba de completar. Gracias. – malex

Cuestiones relacionadas