2009-07-13 14 views
5

Mi sto lentamente insegnando cacao per l'iPhone (attraverso il Stanford Class on iTunes U) e ho appena passato la parte sulla gestione della memoria, e volevo sperare di ottenere qualche conferma che le ipotesi Sto facendo su come viene gestita la memoria e come funzionano [rilascio] e [autorelease]. Poiché la gestione della memoria è una parte davvero fondamentale e fondamentale, ma molto essenziale dell'esperienza di programmazione, mi piacerebbe essere sicuro di farlo nel modo giusto.rilascio/confusione autorelease nel cacao per iphone

Comprendo che qualsiasi cosa con allocazione, nuova o copia deve essere rilasciata.
Se faccio questo:

NSString *temp = [[NSString alloc] initWithString:@"Hello World"]; 

poi ho bisogno di aggiungere [rilascio temp/autorelease] alla fine, dal momento che ho un alloc.

Tuttavia, se faccio questo:

NSString *temp = @"Hello World"; 

Allora non sembra avere bisogno di un comunicato stampa. La classe NSString chiama automaticamente l'autorelease come parte del compito?

Inoltre, c'è una differenza tra i due oggetti temporanei qui dopo queste istruzioni? Entrambi contengono la stessa stringa, ma ci sono modi di memoria/utilizzo in cui differiscono?

In secondo luogo, con le proprietà, presumo che l'autorelease sia gestito automaticamente. Se ho questo:

@interface Person : NSObject 
{ 
    //ivars 
    NSString *firstName; 
    NSString *lastName; 
} 

//properties 
@property NSString *firstName; 
@property NSString *lastName; 

///next file 

@implementation Person 

@synthesize firstName; 
@synthesize lastName; 

- (void) dealloc 
{ 

    //HERE!!!! 

    [super dealloc]; 
} 

Sto assumendo non ho bisogno di aggiungere [firstName rilascio] e [rilascio Cognome] (in // QUI !!!!), dal momento che è gestita automaticamente dalle proprietà . È corretto?

capisco che se faccio questo nel codice (supponendo ho definito initWithFirstName):

Person *Me = [[Person alloc] initWithFirstName: @"Drew", lastName:"McGhie"]; 

che più tardi ho intenzione di dover usare [Me rilascio/autorelease];

Qualsiasi aiuto che conferma o corregge la mia comprensione fino ad ora è molto apprezzato.

POST RISPOSTA write-up

ho pensato di scrivere questo tutto in su dopo essere andato oltre tutte le risposte e testare i suggerimenti e parlare di quello che ha funzionato.

Ho bisogno di aggiungere il [firstName release], [lastname release], ma ho anche bisogno di aggiungere (conservare) alle descrizioni delle proprietà. Non aggiungere gli avvisi (conservati) causati perché presuppone (assegnare). Ecco come ho finalmente creato la classe

@interface Person : NSObject 
    { 
     //ivars 
     NSString *firstName; 
     NSString *lastName; 
    } 

    //properties 
    @property (retain) NSString *firstName; 
    @property (retain) NSString *lastName; 

    ///next file 

    @implementation Person 

    @synthesize firstName; 
    @synthesize lastName; 

    - (void) dealloc 
    { 
     [firstName release]; 
     [lastName release]; 
     [super dealloc]; 
    } 

risposta

10

La regola è semplice: se si alloc, copy o retain, è responsabilità dell'utente release. Se non l'hai fatto, non lo è. Tuttavia, se è necessario fare affidamento su un oggetto che si trova in giro, è necessario retain (e successivamente release).

Possiamo trattare la stringa letterale in base alle regole - non è necessario per lo release perché non lo possiedi. È semplice; non è necessario preoccuparsi se si tratti di casi speciali o meno, basta seguire le regole e starai bene.

Ho scritto un post sul blog con una raccolta di articles about the Cocoa memory management rules; Consiglierei di seguire alcuni dei riferimenti.

1

Ultima parte prima: sarà infatti necessario auto/rilasciare Me. Tuttavia, dovrai aggiungere [firstName release]; e [lastName release]; in -dealloc; o meglio ancora; self.firstName = nil;

Come per le stringhe letterali; questa parte diventa un po 'pelosa, ma lo [@"String literal" release] è essenzialmente un noop.In quanto tale, lo è una differenza tra i due oggetti temporanei, ma poiché generalmente non si conosce quale sarà il problema, aggiungere [temp release]; è generalmente la scelta sicura, a meno che tu sappia che conterrà un oggetto autoreleased.

+0

Non alloco/copio/conservo in modo esplicito firstName o lastName. È necessario perché l'allocazione è dedotta dalla generazione automatica di sintesi che non vedo nel codice? –

+0

In un certo senso. Il problema è che il tuo oggetto "possiede" 'firstName' e' lastName', quindi quando sono impostati, diventa tua responsabilità rilasciarli. –

3
  1. Non ho mai rilasciato costanti di stringa come NSString *foo = @"x";. Logicamente, se dovessi ottenere il risultato di release, dovresti release il parametro a initWithString, ed entrambi i parametri a initWithFirstName:lastName:, anche.
  2. È necessario eseguire release o autoreleasefirstName e lastName. Ho visto degli avvertimenti sul fatto di non usare la sintassi delle proprietà nei distruttori, che penso sia la stessa ragione per cui non si usano le funzioni virtuali nei costruttori e nei distruttori di C++.

La tua ipotesi era sbagliata. È necessario effettuare una tale:

Person *Me = [[Person alloc] initWithFirstName: @"Drew" 
              lastName: @"McGhie"]; 
    ... 
    [Me release]; 

o questo:

Person *Me = [Person personWithFirstName: @"Drew" 
            lastName: @"McGhie"]; 

... e assicurarsi che il proprio oggetto Person gestisce +personWithFirstName:lastName: correttamente, vale a dire [[[self alloc] initWithFirstName: firstName lastName: lastName] autorelease].

Probabilmente dovresti fare quello con meno codice. La chiarezza è importante, NSAutoreleasePool probabilmente non sarà mai il collo di bottiglia, e se mai lo sarà sarà facilmente riparato.

Penso che la gente abbia fatto un grande sforzo per evitare i messaggi di classe che restituiscono un oggetto "autorelease" che non è meritato. È un'ottimizzazione prematura, in quanto probabilmente non è necessaria e potrebbe anche non essere la cosa giusta da fare. Ed è più difficile da mantenere, molto probabilmente starai a caccia di perdite per sempre.

Inoltre, si sta andando a autorelease un oggetto si doveva init (vale a dire alloc + initPersonWithFirstName:lastName: invece di utilizzare un messaggio di classe come personWithFirstName:lastName:), suggerirei di farlo immediatamente. Altrimenti, stai potenzialmente inseguendo quello stesso tipo di perdita. Quindi, se non avete intenzione di aggiungere un metodo personWithFirstName:lastName:-Person, fare questo, invece:

Person *Me = [[[Person alloc] initWithFirstName: @"Drew" 
              lastName: @"McGhie"] autorelease]; 

Sommario: cacao fa molto per aiutare con la gestione della memoria. Assicurati di non combatterlo.

Aggiornato in base al feedback di Jon nel commento.

+0

Piuttosto che [[[Person alloc] initWithFirstName: firstName lastName: lastName] autorelease], [[[auto allocazione] initWithFirstName: firstName lastName: lastName] autorelease] sarebbe più appropriato. Usando il sé, assegnerai il tipo corretto di oggetto se c'è una sottoclasse di una persona. Ad esempio: [SpecialPerson personWithFirstName: @ "Drew" lastName: @ "McGhie"] crea correttamente un'istanza di una persona speciale. In un metodo "+", self si riferisce alla classe che possiede il metodo. –

+0

Questo è un * fantastico * suggerimento, Jon. Controllerà e aggiornerà la risposta. Grazie! –

1

Informazioni su firstName/lastName.

Si dovrebbe sempre, per chiarezza, ricordare di specificare gli attributi delle proprietà. L'attributo predefinito setter è assegnare, in questo caso si desidera utilizzare conservare.

@interface Person : NSObject 
{ 
    NSString *firstName; 
} 
@property (retain) NSString *firstName; 
@end 

Con trattenere ciascuno e solo volta che si utilizza la notazione punto per assegnare un valore il compilatore inserisce una conservano: basta ricordarsi di usare sempre esso. Per coerenza consiglio che di scrivere il vostro initializer in questo modo:

- (id) initWithFirstName:(NSString*) aString 
{ 
    self.firstName = aString; 
} 

e il metodo dealloc in questo modo:

- (void) dealloc 
{ 
    self.firstName = nil; 
} 

Chi @ "" - gli oggetti di tipo. Sono oggetti costanti di NSStrings. Basta usarli come erano (sono) oggetti NSString e non li rilascia mai. Il compilatore si prende cura di loro.

+0

Non dovresti scrivere init/dealloc in questo modo. Questo approccio invierà un messaggio all'oggetto, che può essere gestito da una sottoclasse invece di arrivare alla tua classe. –

+0

Ho sentito che Apple consiglia contro getter/setters in init/dealloc, ma non riesco a trovare i documenti a riguardo. Potresti postare un link? – IlDan

1

La classe NSString chiama automaticamente la trasmissione automatica come parte dell'assegnazione?

La classe NSString non ha fatto nulla perché non è stato inviato un messaggio. Tutto quello che hai fatto è stato assegnare a una variabile. NSString non lo trova e non è affar suo.

Inoltre, c'è una differenza tra i due oggetti temporanei qui dopo queste istruzioni? Entrambi contengono la stessa stringa, ma ci sono modi di memoria/utilizzo in cui differiscono?

Sono entrambi NSStrings, entrambi contengono lo stesso valore e sono entrambi presumibilmente immutabili. Questo è tutto ciò di cui dovresti mai preoccuparti.

In secondo luogo, con le proprietà, suppongo che l'autorelease sia gestito automaticamente. Se ho questo:

@property NSString *firstName; 
@property NSString *lastName; 

- (void) dealloc 
{ 
    //HERE!!!! 

    [super dealloc]; 
} 

Sto assumendo non ho bisogno di aggiungere [firstName release] e [lastName release] (a //HERE!!!!), dal momento che è automaticamente gestito dalla proprietà. È corretto?

No. NSObject non rilascia tutti i valori di proprietà dell'utente; hai ancora bisogno di rilasciarli da soli lì.

Inoltre, non fare self.firstName = nil e self.lastName = nil in dealloc. Quelli si traducono in messaggi (ai tuoi metodi di accesso), e quando lo fai in dealloc, stai inviando messaggi a un mezzo kart dealloc. Questo sta chiedendo guai. Lo stesso vale per l'altro modo di inizializzare i valori delle proprietà in init: Usando le proprietà/gli accessori, si invierà messaggi a un oggetto mezzo-init ed.

2

Le NSStrings create con la sintassi @"String here" sono stringhe costanti. Questi sono diversi dalle stringhe normali.Proprio come le normali stringhe C costanti, vengono create quando il programma viene caricato ed esiste per tutta la sua durata. La classe NXConstantString, a cui appartengono, ignora tutti i messaggi di gestione della memoria. Puoi retain e release che ti piacciono e non farà alcuna differenza.

Per una stringa creata con un metodo [[NSString alloc] initWith...], si applica il normale memory management rules. Consiglio vivamente di leggere i documenti collegati: non sono complicati e, dopo averli letti, saprete praticamente tutto ciò che avrete bisogno di sapere sulla gestione della memoria di Cocoa.