2012-02-10 23 views
37

Al observar un valor en un objeto usando addObserver:forKeyPath:options:context:, eventualmente querrá llamar al removeObserver:forKeyPath: en ese objeto para limpiarlo más tarde. Sin embargo, antes de hacerlo, ¿es posible verificar si un objeto realmente está observando esa propiedad?KVO - ¿Cómo comprobar si un objeto es un observador?

He intentado asegurar en mi código que un objeto solo tiene un observador eliminado cuando debe serlo, pero hay algunos casos en los que es posible que el observador intente eliminarse dos veces. Estoy trabajando para evitar esto, pero por si acaso, he estado tratando de averiguar si hay una forma de verificar primero si mi código realmente es un observador de algo.

+0

KVO ya que tiene una API bastante tosca. Hay bibliotecas disponibles que simplifican su uso e incluso le permiten usar bloques por conveniencia. Visita http://thirdcog.eu/pwcblocks/#goodies para más detalles. También tengo mi propia implementación con la capacidad de eliminar observadores automáticamente cuando cualquiera de los objetos se desasigna. Todavía no se ha probado en aplicaciones reales, pero es posible que desee echar un vistazo de todos modos. Busque 'tastykvo' en GitHub. –

Respuesta

43

[...] ¿es posible verificar si un objeto realmente está observando esa propiedad ?

No. Cuando se trata de MVA que siempre debe tener el siguiente modelo en mente:

Al establecer una observación que es responsable de la eliminación de esa observación exacta. Una observación se identifica por su contexto; por lo tanto, el contexto debe ser único. Al recibir notificaciones (y, en Lion, al eliminar al observador), siempre debe probar el contexto, no la ruta.

La mejor práctica para la manipulación de objetos observados se, para eliminar y establecer la observación en la incubadora del objeto observado:

static int fooObservanceContext; 

- (void)setFoo:(Foo *)foo 
{ 
    [_foo removeObserver:self forKeyPath:@"bar" context:&fooObservanceContext]; 

    _foo = foo; // or whatever ownership handling is needed. 

    [foo addObserver:self forKeyPath:@"bar" options:0 context:&fooObservanceContext]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if (context == &fooObservanceContext) { 
     // handle change 
    } else { 
     // not my observer callback 
     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 
    } 
} 

- (void)dealloc 
{ 
    self.foo = nil; // removes observer 
} 

Al utilizar MVA usted tiene que asegurarse de que ambos objetos, el observador y el observado, están vivos mientras la observación esté en su lugar.

Al agregar una observación, tiene que equilibrar esto con exactamente una eliminación de la misma observación. No asuma, usted es el único que usa KVO. Las clases de Framework pueden usar KVO para sus propios fines, de modo que siempre verifique el contexto en la devolución de llamada.

Una última cuestión que me gustaría señalar: la propiedad observada tiene que ser compatible con KVO. You can't just observe anything.

+0

¿Necesita establecer fooObservanceContext en cualquier cosa, o eso se tiene en cuenta cuando agrega el observador? – jrturton

+2

Se inicializa automáticamente a cero, pero en este caso solo se usa para su dirección. Necesitamos un valor único para el 'contexto'. Como no sabemos qué otras personas usan como contexto, esta parece ser una buena forma de crear un valor que es muy poco probable que se use antes. –

+0

Ok, entonces tiene una dirección porque es estática, y eso es lo que está pasando usando el &? No estoy muy entusiasmado con todo eso, ¡gracias por la explicación! – jrturton

25

Parte del protocolo NSKeyValueObserving es la siguiente:

- (void *)observationInfo 

que debe enumerar los observadores.

EDIT Útil para la depuración solamente.

+1

Estaba escribiendo una respuesta al efecto de que la lista explícitamente no está disponible porque 'observationInfo' está documentado para devolver un puntero opaco (que, de hecho, puede o no ser un objeto). ¿Eso suena exacto? – Tommy

+0

@Tommy Probablemente sea exacto. Estaba yendo por el documento que dice "devuelve un puntero que identifica la información sobre todos los observadores". Sospecho que entiendes esto mucho más profundamente que yo. – Rayfleck

+3

No puede usar esto en el código de producción, pero en el depurador es bastante útil: escriba 'po [observóObjeto observationInfo]' y obtendrá una buena visión general de los observadores y las rutas clave. –

Cuestiones relacionadas