Oggetto allocare e init nell'Obiettivo C
-
03-07-2019 - |
Domanda
Qual è la differenza tra i seguenti 2 modi per allocare e inizializzare un oggetto?
AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];
e
self.aController= [[AController alloc] init];
La maggior parte dell'esempio di apple usa il primo metodo. Perché dovresti allocare, init e obiettare e quindi rilasciare immediatamente?
Soluzione
Ogni oggetto ha un conteggio di riferimento. Quando va a 0, l'oggetto viene deallocato.
Supponendo che la proprietà sia stata dichiarata come @property (keep)
:
Il tuo primo esempio, riga per riga:
- L'oggetto è stato creato da
alloc
, ha un conteggio di riferimento di 1. - L'oggetto viene passato al metodo
self
delsetAController:
, che gli invia un messaggioconserva
(perché il metodo non sapere da dove proviene l'oggetto), incrementando il suo conteggio di riferimento a 2. - Il codice chiamante non ha più bisogno dell'oggetto stesso, quindi chiama
release
, riducendo il conteggio dei riferimenti a 1.
Il tuo secondo esempio sostanzialmente fa i passaggi 1 e 2 ma non 3, quindi alla fine il conteggio dei riferimenti dell'oggetto è 2.
La regola è che se crei un oggetto, sei responsabile di rilasciarlo quando hai finito con esso. Nel tuo esempio, il codice viene eseguito con tempAController dopo aver impostato la proprietà. È responsabilità del metodo setter chiamare trattenere
se è necessario che l'oggetto rimanga.
È importante ricordare che self.property = foo;
in Objective-C è davvero solo una scorciatoia per [self setProperty: foo];
e che setProperty: il metodo
conserverà o copierà gli oggetti secondo necessità.
Se la proprietà fosse stata dichiarata @property (copia)
, l'oggetto sarebbe stato copiato anziché conservato. Nel primo esempio, l'oggetto originale sarebbe stato rilasciato immediatamente; nel secondo esempio, il conteggio dei riferimenti dell'oggetto originale sarebbe 1 anche se dovrebbe essere 0. Quindi vorrai comunque scrivere il tuo codice allo stesso modo.
Se la proprietà è stata dichiarata @property (assegnato)
, allora self
non rivendica la proprietà dell'oggetto e qualcun altro deve mantenerlo. In questo caso, il primo esempio sarebbe errato. Questi tipi di proprietà sono rari, di solito utilizzati solo per i delegati di oggetti.
Altri suggerimenti
Come altri hanno notato, i due frammenti di codice visualizzati non sono equivalenti (per motivi di gestione della memoria). Per quanto riguarda il motivo per cui il primo è scelto rispetto al secondo:
La formulazione corretta di quest'ultimo sarebbe
self.aController= [[[AController alloc] init] autorelease];
Rispetto al primo, questo aggiunge ulteriore sovraccarico attraverso l'uso del pool di autorelease, e in alcune circostanze porterà alla durata dell'oggetto inutilmente estesa (fino al rilascio del pool di autorelease) che aumenterà il footprint di memoria dell'applicazione.
L'altro "possibile" l'implementazione (a seconda della provenienza dell'esempio) è semplicemente:
aController = [[AController alloc] init];
Tuttavia, l'impostazione diretta di una variabile di istanza è fortemente sconsigliata in qualsiasi luogo diverso da un metodo init o dealloc. Altrove dovresti sempre usare i metodi di accesso.
Questo ci porta quindi all'implementazione mostrata nel codice di esempio:
AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];
Segue le migliori pratiche da:
- Evita il rilascio automatico;
- Rende immediatamente chiara la semantica della gestione della memoria;
- Utilizza un metodo di accesso per impostare la variabile di istanza.
Nota anche che il tuo desiderio di ridurre il codice su una riga è il motivo per cui molte persone usano Autorelease:
self.aController = [[[AController alloc] init] autorelease];
Sebbene in teoria il rilascio automatico dell'iPhone sia in qualche modo più costoso (non ho mai sentito una chiara spiegazione del perché) e quindi potresti voler rilasciare esplicitamente subito dopo aver assegnato l'oggetto altrove.
Se stai usando Xcode, può aiutarti a rilevare tale codice con l'analizzatore statico. Premi Build > > Costruisci e analizza
Questo ti mostrerà un messaggio molto utile in tali parti di codice.
Un'altra cosa da notare è che il tuo esempio dipende anche dalla definizione @property di aController.
Se fosse definito come @property (readwrite, keep) id aController;
allora il tuo esempio funziona, mentre se è definito come @property (readwrite, assegnato) id aController;
quindi la chiamata extra da rilasciare causerebbe la deallocazione dell'oggetto.
Puoi anche farlo
@property (nonatomic, retain)AController *aController;
...
self.aController= [[AController alloc] init];
[aController release];
con una proprietà di mantenimento, e funzionerebbe allo stesso modo, ma è meglio usare l'altro modo (per conservare le proprietà) perché è meno confuso, quel codice fa sembrare che tu assegni un Controller e quindi viene eliminato dalla memoria , quando in realtà non lo fa perché setAController lo mantiene.