2012-05-29 8 views
35

Ho una vista (chiameremo questa vista A) che ha una proprietà weak alla sua superview (vista B). Visualizza A KVO's its superview, vedi B. Poiché il riferimento della vista A alla vista B è una proprietà debole (per impedire un ciclo di conservazione), come posso rimuovere l'osservatore (A osservando B)? Vedi il riferimento di A per vedere che B si spegne prima che io abbia la possibilità di rimuoverlo.Come si rimuove KVO da una proprietà debole?

A sopravvive B dal momento che il controller di vista ha un forte riferimento alla A. è il messaggio di log che perde Qui:

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 è un UITableView. L'impostazione di un punto di interruzione su NSKVODeallocateBreak produce risultati inutili.

In A's removeFromSuperview, provo a rimuovere l'osservatore ma il riferimento di A a B è già nil.

Passare a unsafe_unretained e fare le cose più manualmente o chiamando [A removeFromSuperview] nel controller della vista dealloc risolve il problema. Mi piacerebbe sapere come risolvere questo utilizzando una proprietà weak però.

Ecco il codice rilevante: https://gist.github.com/2822776

+0

Il mio male ... +1 comunque. – CodaFi

risposta

1

Si potrebbe definire una proprietà debole esplicito riferimento al superview e poi osservare self con un percorso chiave come @"propertyReferringSuperview.propertyOfSuperview"? Quando ricevi una notifica KVO, controlli se self.propertyReferringSuperview == nil e smetti di osservare @"propertyReferringSuperview.propertyOfSuperview".

+1

Ho provato questo approccio. Quando il debole riferimento alla superview viene annullato, KVO non spara. –

+0

Che vergogna ... Forse provate a inserire la pulizia nel setter personalizzato 'setPropertyReferringSuperview:'? Spero che venga chiamato quando il riferimento debole diventa "nullo". In realtà, puoi iniziare e smettere di osservare proprio in questo setter. – Stream

+0

Questa è un'ottima risposta. Non è necessario rimuovere te stesso come osservatore quando la superview viene annullata. Puoi rimanere un osservatore finché non sei stato disallocato. Quindi, registrati per ascoltare in init (anche se la tua proprietà è nulla, va bene), quindi annulla la registrazione in dealloc (o deinit). – plivesey

2

trovo nessun tipo di codice richiesto appositamente per questo caso davvero inutile come la rimozione può essere automatizzato.

Con l'introduzione di ARC, Apple dovrebbe avere fornire la rimozione automatica degli osservatori che avrebbero risolvere casi come questo, ma purtroppo non lo fecero. Ma ho fatto il mio categoria che aggiunge questa caratteristica mancante: https://github.com/krzysztofzablocki/SFObservers ho spiegato come ho fatto gestire tale sul mio blog: http://www.merowing.info/2012/03/automatic-removal-of-nsnotificationcenter-or-kvo-observers/

Se guardate la mia soluzione si noterà, che fa in modo che originale viene chiamato il codice, anche se uno dei metodi chiama altri, in modo che anche se Apple modifica il suo comportamento interno, la categoria funzionerà ancora bene :)

+0

I tuoi SFOvservers sembrano fantastici. Speravo di usare una soluzione meno intelligente. Le cose Swizzling su NSObject sono un po 'spaventose. Chissà cosa cambieranno in iOS 6. –

+0

Ecco perché la mia soluzione non assume alcun ordine di metodi originali e invece chiama il codice originale :-) Quindi è una prova futura se cambiano qualcosa. –

0

Invece di aggiungere una proprietà debole, è sufficiente utilizzare la proprietà superview e attuare willMoveToSuperview: per aggiungere/rimuovere l'osservazione 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

Quando viene chiamato 'willMoveToSuperview:', la vista di riferimento della proprietà debole B (o 'scrollView' nel Gist) è già' nil'. –

+0

Sì ma hai detto che la vista B è la superview di A. Quindi non hai bisogno della proprietà 'scrollView'. Basta usare 'superview'. –

Problemi correlati