Una clase tiene una propiedad (y var de instancia) de tipo NSMutableArray con descriptores de acceso sintetizados (via @property
). Si observa esta matriz usando:
[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];
Y luego inserte un objeto en la matriz como este:
[myObj.theArray addObject:NSString.string];
Un observeValueForKeyPath ... notificación no se ha enviado. Sin embargo, lo siguiente 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 accesores sintetizados devolver automáticamente tal objeto proxy? ¿Cuál es la forma correcta de evitar esto? ¿Debería escribir un descriptor de acceso personalizado que simplemente invoque [super mutableArrayValueForKey...]
?
fuente
No lo usaría
willChangeValueForKey
ydidChangeValueForKey
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. En suwillChange:valuesAtIndexes:forKey:
lugar, querría usarlo si lo hiciera de esta manera. Aun así, el uso de notificaciones KVO manuales como esta es una mala encapsulación. Una mejor forma de hacerlo es definir un métodoaddSomeObject:
en la clase que realmente posee la matriz, que incluiría las notificaciones KVO manuales. De esta manera, los métodos externos que agregan objetos a la matriz no necesitan preocuparse por manejar el KVO del propietario de la matriz también, lo cual no sería muy intuitivo y podría generar código innecesario y posiblemente errores si comienza a agregar objetos a la matriz. Matriz 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, este método en realidad reemplaza toda la matriz con un nuevo objeto, por lo que si el rendimiento es una preocupación, también querrá implementarinsertObject:in<Key>AtIndex:
yremoveObjectFrom<Key>AtIndex:
en la clase que posee la matriz. .fuente
cuando solo desee observar el cambio en el recuento, puede usar una ruta de clave agregada:
[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];
pero tenga en cuenta que no se activará ningún reordenamiento en el Array.
fuente
Tu propia respuesta a tu propia pregunta es casi correcta. No venda
theArray
externamente. En su lugar, declare una propiedad diferente,theMutableArray
que no corresponda a ninguna variable de instancia, y escriba este descriptor de 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 activan KVO.Las otras respuestas señalan que la eficiencia aumenta si también implementa
insertObject:inTheArrayAtIndex:
yremoveObjectFromTheArrayAtIndex:
sigue siendo correcto. Pero no es necesario que otros objetos tengan que conocerlos o llamarlos directamente.fuente
theMutableArray
como si fuera unaNSMutableArray
propiedad.Si no necesita el setter, 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 descriptores de acceso a la matriz no están implementados y no hay un definidor 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 acceso para colecciones ordenadas", n.º 3.
fuente
readonly
, o de lo contrario, se atascará en un bucle infinito ...Necesita completar su
addObject:
llamadawillChangeValueForKey:
y susdidChangeValueForKey:
llamadas. Hasta donde yo sé, no hay forma de que el NSMutableArray que está modificando sepa si hay observadores que observan a su propietario.fuente
una solución es usar un NSArray y crearlo desde cero insertando y quitando, 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; }
de lo que obtiene el KVO y puede comparar la matriz antigua y la nueva
NOTA: self.myArray no debe ser nulo, de lo contrario arrayByAddingObject: también da como resultado nil
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 arreglos grandes y operaciones frecuentes.
fuente