Devo impostare le proprietà su zero in dealloc quando utilizzo ARC?


125

Sto cercando di imparare il conteggio automatico dei riferimenti in iOS 5. Ora la prima parte di questa domanda dovrebbe essere semplice:

  1. È corretto che NON devo scrivere istruzioni esplicite sulla proprietà di rilascio nel mio dealloc quando utilizzo ARC? In altre parole, è vero che quanto segue NON necessita di un dealloc esplicito?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
  2. La mia domanda successiva e più importante viene da una riga del documento Transitioning to ARC Release Notes :

    Non è necessario (anzi impossibile) rilasciare variabili di istanza, ma potrebbe essere necessario richiamare [self setDelegate: nil] su classi di sistema e altro codice che non è stato compilato utilizzando ARC.

    Questo pone la domanda: come faccio a sapere quali classi di sistema non sono compilate con ARC? Quando dovrei creare il mio dealloc e impostare esplicitamente le proprietà di mantenimento su zero? Devo supporre che tutte le classi di framework NS e UI utilizzate nelle proprietà richiedano dealloc espliciti?

Esistono molte informazioni su SO e altrove sulle pratiche di rilascio di ivar di supporto di una proprietà quando si utilizza il tracciamento manuale dei riferimenti, ma relativamente poco su questo quando si utilizza ARC.

Risposte:


197

Risposta breve : no, non è necessario azzerare le proprietà in deallocARC.

Risposta lunga : non si dovrebbe mai azzerare le proprietà dealloc, anche nella gestione manuale della memoria.

In MRR, è necessario rilasciare gli ivar . Compilare le proprietà significa chiamare i setter, che possono invocare il codice che non dovrebbero toccare dealloc(ad esempio se la tua classe, o una sottoclasse, sovrascrive il setter). Allo stesso modo può innescare le notifiche KVO. Rilasciare l'ivar invece evita questi comportamenti indesiderati.

In ARC, il sistema rilascia automaticamente qualsiasi ivar per te, quindi se è tutto ciò che stai facendo non devi nemmeno implementarlo dealloc. Tuttavia, se hai degli ivar non oggetto che necessitano di una gestione speciale (ad es. Buffer allocati di cui hai bisogno free()) devi comunque gestirli dealloc.

Inoltre, se ti sei impostato come delegato di qualsiasi oggetto, dovresti annullare l'impostazione di quella relazione dealloc(questo è il problema della chiamata [obj setDelegate:nil]). La nota su come farlo su classi che non sono compilate con ARC è un cenno verso proprietà deboli. Se la classe contrassegna esplicitamente la sua delegateproprietà come weakallora non è necessario farlo, perché la natura delle proprietà deboli significa che verrà annullata per te. Tuttavia, se la proprietà è contrassegnata, assignè necessario annullarla nel proprio dealloc, altrimenti la classe viene lasciata con un puntatore penzolante e probabilmente si bloccherà se tenta di inviare un messaggio al suo delegato. Si noti che ciò si applica solo alle relazioni non mantenute, come i delegati.


2
Questo ha senso! Lascia che ti chieda questo: uno scenario comune che ho è che ho una MyController : UIViewControllerclasse che crea e possiede un UIView e imposta anche il delegato della vista su se stesso. È il solo proprietario di tale punto di vista. Quando il controller viene deallocato, anche la vista dovrebbe essere deallocata. Importa quindi se il puntatore delegato pende?
emfurry

4
@emfurry: Probabilmente no, perché quando il controller della vista muore la vista stessa non dovrebbe essere nella gerarchia della vista e non dovrebbe fare nulla, ma è meglio non fare ipotesi. Cosa succede se il lavoro programmato in modo asincrono della vista deve essere eseguito in un secondo momento e la vista stessa finisce per sopravvivere per un breve periodo al suo controller di vista (ad es. A causa del lavoro asincrono che mantiene temporaneamente la vista)? È meglio solo azzerare il delegato per essere al sicuro. E in effetti, se la vista in questione è a UIWebView, i documenti dichiarano esplicitamente che è necessario azzerare il delegato.
Lily Ballard,

3
@zeiteisen: No. unsafe_unretainedequivale esattamente a una assignproprietà ed è il comportamento normale per le relazioni delegate in MRR, e queste devono essere completate.
Lily Ballard

4
Non sono d'accordo con l'affermazione sul non usare setter in dealloc con MRC. Apple non lo consiglia, ma lo fanno anche nel loro codice. Puoi effettivamente creare nuovi problemi non usando il setter. Ci sono molte grandi discussioni a riguardo. Ciò che è importante è scrivere correttamente il setter (deve comportarsi correttamente se gli si passa un valore nullo) e talvolta osservare l'ordine di deallocazione.
Sulthan

7
@Sulthan: L'uso o meno dei setter in dealloc è un'enorme lattina di worm, ma la mia posizione si riduce sostanzialmente a: vuoi chiamare il meno codice possibile in dealloc. I setter hanno la tendenza ad includere effetti collaterali, sia scavalcando le sottoclassi, sia via KVO, o altri meccanismi. Gli effetti collaterali nel dealloc dovrebbero essere evitati in particolare come la peste. Se è possibile rimuovere una chiamata di metodo da dealloc, è necessario farlo. Questo è semplificato fino a: non chiamare setter in dealloc.
Lily Ballard

2

Giusto per dare la risposta opposta ...

Risposta breve : no, non è necessario azzerare le proprietà sintetizzate automaticamente in deallocARC. E non devi usare il setter per quelli in init.

Risposta lunga : è necessario eliminare le proprietà sintetizzate in modo personalizzato dealloc, anche in ARC. E dovresti usare il setter per quelli in init.

Il punto è che le tue proprietà sintetizzate su misura dovrebbero essere sicure e simmetriche per quanto riguarda l'annullamento.

Un possibile setter per un timer:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

Un possibile setter per scrollview, tableview, webview, textfield, ...:

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

Un possibile setter per una proprietà KVO:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

Quindi non c'è bisogno di duplicare qualsiasi codice per dealloc, didReceiveMemoryWarning, viewDidUnload, ... e la vostra proprietà può tranquillamente essere reso pubblico. Se eri preoccupato di azzerare le proprietà dealloc, allora potrebbe essere il momento di controllare di nuovo i tuoi setter.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.