2013-04-20 9 views
10

ho notato quanto segue in Objective-C con ARC abilitato:proprietà debole è impostata a zero in dealloc ma Ivar della struttura non è nullo

Diamo semplice classe A e autosynthesized proprietà debole

@interface A 
@property (nonatomic, weak) id refObject; 
@end 

@implementation A 
@end 

E seconda classe B con dealloc implementato

@interface B 
@end 

@implementation B 
-(void) dealloc 
{ 
    NSLog(@"In dealloc"); 
} 
@end 

E infine qualche parte in classe a sono i seguenti:

@implementation A 
... 
-(void) foo 
{ 
    B* b = [B new]; 
    self.refObject = b; 
    // Just use b after the weak assignment 
    // in order to not dealloc 'b' before assignement 
    NSLog(@"%@", b); 
} 
... 
@end 

Se ho impostato un punto di interruzione in [B dealloc] e controllare [A refObject] proprietà Vedo che a.refObject è pari a zero, ma a->_refObject non è pari a zero e punti a 'b'

Tutte le idee perché ciò accade?

+0

Forse il metodo accessor restituisce 'nil' quando sa che il ref debole deve essere cancellato, ma la variabile di istanza stessa viene lasciata intatta (e deallocata, e ora è un puntatore pendente). –

+0

Penso che in dealloc l'oggetto sia ancora valido e non ancora cancellato. Ad esempio, si annulla la registrazione da NSNotificationCenter e si può accedere alle sue proprietà. –

+0

"puoi accedervi" non significa necessariamente che non sia stato rilasciato, ma potrei sbagliarmi. –

risposta

22

Risposta breve: L'istanza variabile a->_refObject non è (ancora) nulla in -[B dealloc], ma ogni accesso a tale puntatore debole viene fatto attraverso una funzione ARC runtime che restituisce nil se la deallocazione è già iniziata.

Risposta lunga: Impostando un watchpoint si può vedere che a->_refObject è impostato a zero al termine del processo di deallocazione. Il backtrace dello stack (quando il watchpoint viene colpito) si presenta così:

frame #0: 0x00007fff8ab9f0f8 libobjc.A.dylib`arr_clear_deallocating + 83 
frame #1: 0x00007fff8ab889ee libobjc.A.dylib`objc_clear_deallocating + 151 
frame #2: 0x00007fff8ab88940 libobjc.A.dylib`objc_destructInstance + 121 
frame #3: 0x00007fff8ab88fa0 libobjc.A.dylib`object_dispose + 22 
frame #4: 0x0000000100000b27 weakdealloc`-[B dealloc](self=0x000000010010a640, _cmd=0x00007fff887f807b) + 151 at main.m:28 
frame #5: 0x0000000100000bbc weakdealloc`-[A foo](self=0x0000000100108290, _cmd=0x0000000100000e6f) + 140 at main.m:41 
frame #6: 0x0000000100000cf5 weakdealloc`main(argc=1, argv=0x00007fff5fbff968) + 117 at main.m:52 
frame #7: 0x00007fff8c0987e1 libdyld.dylib`start + 1 

e object_dispose() è chiamato da -[NSObject dealloc] (come si può vedere in http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm).

Pertanto in -[B dealloc], a->_refObject non è nullo prima che venga generato il (compilatore generato) [super dealloc].

Quindi la domanda rimane: perché a.refObject restituisce nulla a quel punto?

Il motivo è che per ogni accesso a un puntatore debole il compilatore ARC genera una chiamata a objc_loadWeak() o objc_loadWeakRetained(). Dal documentation:

id objc_loadWeakRetained (id * object)

Se oggetto viene registrato come un oggetto __weak, e l'ultimo valore memorizzato in oggetto non è> stato ancora deallocato o deallocazione iniziata, conserva quel valore e lo restituisce. Altrimenti> restituisce null.

Quindi, anche se a->refObject non è nullo, a quel punto, l'accesso al puntatore deboli via objc_loadWeakRetained() (come fatto con il metodo di accesso di proprietà) restituisce nil, perché la deallocazione dell'oggetto B è già iniziata.

Il debugger accede direttamente a a->refObject e non chiama objc_loadWeak().

+0

Grazie. Ho pensato che ogni volta che si accede al puntatore debole il compilatore aggiunge objc_loadWeak ma sembra che non sia il caso di un -> _ refObj –

+0

@plamkata__: Il * compilatore * aggiunge 'objc_loadWeak [Mantenuto]' per ogni accesso a 'a-> refObject' , solo il * debugger * accede direttamente a ivar. –

+0

Ha perfettamente senso. Avevo l'impressione che quando osservavo quel comportamento stavo usando NSLog nel metodo dealloc per scaricare l'ivar e questo era ciò che mi confondeva. Ma in realtà non stavo usando NSLog ma il debugger invece così come hai detto accedervi direttamente. Grazie ancora per l'aiuto. –