cocoa - Observando un NSMutableArray para inserción/eliminación
key-value-observing key-value-coding (7)
Pero, ¿no deberían los accesadores sintetizados devolver automáticamente dicho objeto proxy?
No.
¿Cuál es la forma correcta de evitar esto? ¿Debo escribir un acceso personalizado que invoque
[super mutableArrayValueForKey...]
?
No. Implementa los accesadores de la matriz . Cuando los llame, KVO publicará las notificaciones apropiadas automáticamente. Entonces todo lo que tienes que hacer es:
[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];
y lo correcto sucederá automáticamente.
Para su comodidad, puede escribir addTheArrayObject:
accessor. Este descriptor de acceso llamaría a uno de los accesadores de arreglo real descritos anteriormente:
- (void) addTheArrayObject:(NSObject *) newObject {
[self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]];
}
(Puede y debe completar la clase adecuada para los objetos en la matriz, en lugar de NSObject
).
Entonces, en lugar de [myObject insertObject:…]
, escribe [myObject addTheArrayObject:newObject]
.
Lamentablemente, add<Key>Object:
y su contraparte remove<Key>Object:
última vez que lo verifiqué, solo KVO reconoce las propiedades establecidas (como en NSSet), no las propiedades de matriz, por lo que no obtienes notificaciones KVO gratuitas con ellos, a menos que los implemente encima de los accesorios, sí los reconoce. Archivé un error sobre esto: x-radar: // problem / 6407437
Tengo una lista de todos los formatos de selector de acceso en mi blog.
Una clase tiene una propiedad (y instancia var) de tipo NSMutableArray con accesores sintetizados (a través de @property
). Si observa esta matriz usando:
[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];
Y luego inserta un objeto en la matriz de esta manera:
[myObj.theArray addObject:NSString.string];
No se envía una notificación observeValueForKeyPath ... Sin embargo, lo siguiente sí envía la notificación adecuada:
[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];
Esto se debe a que mutableArrayValueForKey
devuelve un objeto proxy que se encarga de notificar a los observadores.
Pero, ¿no deberían los accesadores sintetizados devolver automáticamente dicho objeto proxy? ¿Cuál es la forma correcta de evitar esto? ¿Debo escribir un acceso personalizado que invoque [super mutableArrayValueForKey...]
?
No usaría willChangeValueForKey
y didChangeValueForKey
en esta situación. Por un lado, están destinados a indicar que el valor en esa ruta ha cambiado, no que los valores en una relación a muchos estén cambiando. willChange:valuesAtIndexes:forKey:
usar willChange:valuesAtIndexes:forKey:
cambio, si lo hizo de esta manera. Aun así, usar notificaciones KVO manuales como esta es una mala encapsulación. Una forma mejor de hacerlo es definir un método addSomeObject:
en la clase que realmente posee la matriz, que incluiría las notificaciones manuales de KVO. De esta forma, los métodos externos que agregan objetos a la matriz no necesitan preocuparse por manejar también el KVO del propietario de la matriz, lo que no sería muy intuitivo y podría generar código innecesario y posiblemente errores si comienza a agregar objetos a la matriz. conjunto de varios lugares.
En este ejemplo, continuaría usando mutableArrayValueForKey:
No estoy seguro con las matrices mutables, pero creo que al leer la documentación de que este método realmente reemplaza toda la matriz con un nuevo objeto, si el rendimiento es una preocupación, también querrás implementar insertObject:in<Key>AtIndex:
y removeObjectFrom<Key>AtIndex:
en la clase que posee la matriz.
Si no necesita el colocador, también puede usar el formulario más simple a continuación, que tiene un rendimiento similar (la misma tasa de crecimiento en mis pruebas) y menos repetitivo.
// Interface
@property (nonatomic, strong, readonly) NSMutableArray *items;
// Implementation
@synthesize items = _items;
- (NSMutableArray *)items
{
return [self mutableArrayValueForKey:@"items"];
}
// Somewhere else
[myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items"
Esto funciona porque si los accessors de la matriz no están implementados y no hay un setter para la clave, mutableArrayValueForKey:
buscará una variable de instancia con el nombre _<key>
o <key>
. Si encuentra uno, el proxy reenviará todos los mensajes a este objeto.
Consulte estos documentos de Apple , sección "Patrón de búsqueda de accesorios para colecciones ordenadas", n. ° 3.
Tu propia respuesta a tu propia pregunta es casi correcta. No theArray
el theArray
externamente. En su lugar, declare una propiedad diferente, theMutableArray
, que corresponde a ninguna variable de instancia, y escriba este theMutableArray
acceso:
- (NSMutableArray*) theMutableArray {
return [self mutableArrayValueForKey:@"theArray"];
}
El resultado es que otros objetos pueden usar thisObject.theMutableArray
para realizar cambios en la matriz, y estos cambios desencadenan KVO.
Las otras respuestas señalan que la eficiencia aumenta si también implementa insertObject:inTheArrayAtIndex:
y removeObjectFromTheArrayAtIndex:
siguen siendo correctas. Pero no es necesario que otros objetos tengan que saber sobre esto o llamarlos directamente.
Una solución es usar un NSArray y crearlo desde cero insertando y eliminando, como
- (void)addSomeObject:(id)object {
self.myArray = [self.myArray arrayByAddingObject:object];
}
- (void)removeSomeObject:(id)object {
NSMutableArray * ma = [self.myArray mutableCopy];
[ma removeObject:object];
self.myArray = ma;
}
que obtienes el KVO y puedes comparar una matriz vieja y nueva
NOTA: self.myArray no debe ser nil, sino arrayByAddingObject: resulta nulo también
Dependiendo del caso, esta podría ser la solución, y como NSArray solo almacena punteros, esto no es una sobrecarga, a menos que trabaje con grandes arreglos y operaciones frecuentes.
cuando solo quiere observar el recuento cambiado, puede usar una ruta de clave agregada:
[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];
pero tenga en cuenta que cualquier reordenamiento en Array no se disparará.
addObject:
ajustar su addObject:
invocar willChangeValueForKey:
y didChangeValueForKey:
llamadas. Por lo que yo sé, no hay forma de que el NSMutableArray que está modificando sepa sobre los observadores que observan a su propietario.