2012-05-29 10 views
35

Tengo una vista (llamaremos a esta vista A) que tiene una propiedad weak en su supervista (ver B). Ver la vista superior de A KVO, vista B. Dado que la referencia de la vista A para ver B es una propiedad débil (para evitar un ciclo de retención), ¿cómo puedo eliminar al observador (A observando a B)? Ver la referencia de A para ver B se anula antes de que tenga la oportunidad de eliminarlo.¿Cómo se elimina KVO de una propiedad débil?

A sobrevive B desde el controlador de vista tiene una fuerte referencia a A. Aquí está el mensaje de registro de fugas:

An instance 0x9ac5200 of class UITableView was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info: 
<NSKeyValueObservationInfo 0x8660360> (
<NSKeyValueObservance 0x8660320: Observer: 0x8660020, Key path: contentOffset, Options: <New: YES, Old: NO, Prior: NO> Context: 0x8660020, Property: 0x864ac80> 
) 

B es un UITableView. Establecer un punto de interrupción en NSKVODeallocateBreak produce resultados inútiles.

En A removeFromSuperview, intento eliminar al observador pero la referencia de A a B ya es nil.

Cambiando a unsafe_unretained y hacer las cosas más manualmente o llamando al [A removeFromSuperview] en la vista del controlador dealloc resuelve el problema. Sin embargo, me gustaría saber cómo resolver esto usando una propiedad weak.

Aquí está el código correspondiente: https://gist.github.com/2822776

+0

My bad ... +1 de todos modos. – CodaFi

Respuesta

1

Se podría definir una propiedad débil explícita referencia a la supervista y luego observar self con una ruta clave como @"propertyReferringSuperview.propertyOfSuperview"? Cuando reciba una notificación de KVO, marque self.propertyReferringSuperview == nil y deje de observar @"propertyReferringSuperview.propertyOfSuperview".

+1

He intentado este enfoque. Cuando se elimina la referencia débil a la supervista, KVO no se activa. –

+0

Qué vergüenza ... Tal vez intente insertar la limpieza en el setter personalizado 'setPropertyReferringSuperview:'? Espero que se llame cuando la referencia débil se convierte en 'nil'. En realidad, puedes comenzar y dejar de observar en este setter. – Stream

+0

Esta es una gran respuesta. No necesitas retirarte como observador cuando la supervista queda anulada. Puedes permanecer como observador hasta que te deshagan de él. Entonces, solo regístrese para escuchar en init (incluso si su propiedad es nula, esto está bien), luego anule el registro en dealloc (o deinit). – plivesey

2

Encuentro que cualquier tipo de código requerido especialmente para este caso es realmente innecesario ya que la eliminación puede ser automatizada.

Con la introducción de ARC, Apple debería haber proporcionado la eliminación automática de observadores que solucionaría casos como este, pero desafortunadamente no lo hicieron. Pero yo he hecho mi propia categoría que añade que carecen de esta característica: https://github.com/krzysztofzablocki/SFObservers he explicado cómo lo hice manejar eso en mi blog: http://www.merowing.info/2012/03/automatic-removal-of-nsnotificationcenter-or-kvo-observers/

Si nos fijamos en mi solución que se dará cuenta, que se asegura de que originales se llama al código, incluso si uno de los métodos llama a otros, por lo que incluso si Apple cambia su comportamiento interno, la categoría seguirá funcionando bien :)

+0

Tus servidores SFOv se ven geniales. Esperaba usar una solución menos inteligente. Swizzling cosas en NSObject es un poco de miedo. Quién sabe qué cambiarán en iOS 6. –

+0

Es por eso que mi solución no adopta ningún orden de métodos originales y en su lugar llama al código original :-) Así que eso es una prueba de futuro si cambian algo. –

0

En lugar de agregar una propiedad débil, puede usar la propiedad superview y implementar willMoveToSuperview: para agregar/eliminar la observación de KVO.

- (void)willMoveToSuperview:(UIView *)newSuperview { 
    [self.superview removeObserver:self forKeyPath:@"contentOffset" context:context]; 
    [newSuperview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:context]; 
    [super willMoveToSuperview:newSuperview]; // optional as default implementation does nothing 
} 
+0

Cuando se llama a 'willMoveToSuperview:', la propiedad débil que hace referencia a la vista B (o 'scrollView' en el Gist) ya es' nil'. –

+0

Sí, pero usted dijo que la vista B es la supervista de A. Por lo tanto, no necesita la propiedad 'scrollView'. Solo usa 'superview'. –

Cuestiones relacionadas