2009-03-04 14 views
8
//creates memory leak 
    self.editMyObject = [[MyObject alloc] init]; 

//does not create memory leak 
    MyObject *temp = [[MyObject alloc] init]; 
    self.editMyObject = temp; 
    [temp release]; 

La prima riga di codice crea una perdita di memoria, anche se si esegue [self.editMyObject release] nel metodo dealloc della classe. self.editMyObject è di tipo MyObject. La seconda riga non causa perdite di memoria. La prima riga è errata o esiste un modo per liberare la memoria?Perché questo crea una perdita di memoria (iPhone)?

+0

Tre buone risposte. Nota bene chi dare la risposta anche. – 4thSpace

risposta

10

Il comportamento corretto dipende dalla dichiarazione di editMyObject @property. Supponendo che si delcared come

@property (retain) id editMyObject; //id may be replaced by a more specific type 

o

@property (copy) id editMyObject; 

quindi l'assegnazione tramite self.editMyObject = conserva o copia l'oggetto assegnato. Dal momento che [[MyObject alloc] init] restituisce un oggetto mantenuto, di cui è il proprietario stesso, si dispone di una conservazione aggiuntiva dell'istanza MyObject e pertanto verrà a mancare a meno che non vi sia una versione corrispondente (come nel secondo blocco). Ti suggerisco di leggere lo Memory Management Programming Guide [2].

Il secondo blocco di codice è corretto, presupponendo che la proprietà sia dichiarata come descritto sopra.

p.s. Non utilizzare [self.editMyObject release] in un metodo -dealloc. Dovresti chiamare [editMyObject release] (supponendo che il supporto di ivar sia chiamato @property è editMyObject). Chiamare la funzione di accesso (tramite self.editMyObject è sicuro per gli accessi sintonizzati @, ma se un accessorio sovrascritto si basa sullo stato dell'oggetto (che potrebbe non essere valido nella posizione di chiamata in -dealloc o causa altri effetti collaterali, si verifica un errore chiamando l'accessor.

[2] regole sulla proprietà oggetto a Cocoa sono molto semplici: se si chiama un metodo che ha alloc, o copy a sua firma (o utilizzare +[NSObject new] che è sostanzialmente equivalente a [[NSObject alloc] init]), allora si "proprio" l'oggetto che viene restituito ed è necessario bilanciare l'acquisizione della proprietà con uno release. In tutti gli altri casi, non si possiede l'oggetto restituito da un metodo. Se si desidera mantenerlo, è necessario assumere la proprietà con uno retain e successivamente rilasciare la proprietà con un release.

+0

Possiedo un NSMutableArray. L'ho impostato per la copia, ma quando faccio questo [self.List addObject: myobject], ottengo un'eccezione non rilevata. Impostarlo per mantenere funziona correttamente. Eventuali suggerimenti? – 4thSpace

+0

Credo che una @property (copia) userà -copy per creare la copia, anche se il nuovo compito è di tipo mutabile.Così si ottiene una copia immutabile (normalmente si usa -mutableCopy per creare una copia mutabile), causando l'eccezione quando si tenta di mutare il valore assegnato .... –

+0

[cont'd] Puoi usare @property (retain) e assegna come self.List = [[mutableArr mutableCopy] autorelease]. –

8

La proprietà viene dichiarata "conservata", ovvero viene conservata automaticamente.

Poiché l'oggetto ha già un conteggio di riferimento di uno da alloc/init, ci sono quindi due riferimenti e sto assumendo solo una versione (nel distruttore).

Fondamentalmente la chiamata a self.editMyObject lo sta facendo davvero;

-(void) setEditMyObject:(MyObject*)obj 
{ 
    if (editMyObject) 
    { 
    [editMyObject release]; 
    editMyObject = nil; 
    } 

    editMyObject = [obj retain]; 
} 
+0

con la differenza che se "editMyObject == obj', questo potrebbe fallire in modo orribile, perché si rilascerebbe l'oggetto (potenzialmente deallocandolo) e quindi si tenterà di mantenere un puntatore deallocato. –

3

La prima versione crea un oggetto senza una versione corrispondente. Quando si assegna l'oggetto, significa che sei un proprietario di quell'oggetto. Il tuo setter conserva presumibilmente l'oggetto (come dovrebbe), il che significa che ora possiedi l'oggetto due volte. È necessario il rilascio per bilanciare la creazione dell'oggetto.

Si dovrebbe leggere the Cocoa memory management guide se si prevede di utilizzare Cocoa affatto. Non è difficile quando lo impari, ma è qualcosa che devi imparare o avrai molti problemi come questo.

1

Chiunque altro ha già ricoperto questo che provoca una perdita di memoria, quindi mi limiterò a carillon con il modo di evitare la 'temperatura' variabile e ancora evitare una perdita di memoria:

self.editMyObject = [[[MyObject alloc] init] autorelease]; 

Questo lascerà la vostra (conservare) proprietà come unico proprietario del nuovo oggetto. Esattamente lo stesso risultato del tuo secondo esempio, ma senza l'oggetto temporaneo.

+0

In un'applicazione desktop, solitamente lo farei. Tuttavia, sull'iPhone (o su qualsiasi ambiente con limitazioni di memoria), l'utilizzo della funzione di autorelease può causare un aumento imprevisto dell'utilizzo della memoria. Attaccare con ritegno/rilascio a meno che non sia necessario autorelease. –

+0

Hmm. Sto sicuramente arrivando dal lato desktop di questo. Puoi spiegare cosa causa il picco? Mi sembra che in questo caso non venga allocata alcuna memoria aggiuntiva, ma la mia esperienza di codifica su iPhone è limitata. –

+0

Non conosco i dettagli ma è ben documentato che l'autorelease è una chiamata piuttosto costosa su iPhone. – 4thSpace

4

Per convenzione in Cocoa e Cocoa-touch, qualsiasi oggetto creato utilizzando [[SomeClass alloc] initX] o [SomeClass newX] viene creato con un conteggio resti di uno. Sei responsabile di chiamare [someClassInstance release] quando hai finito con la nuova istanza, in genere nel tuo metodo dealloc.

Quando ciò risulta complicato è quando si assegna il nuovo oggetto a una proprietà anziché a una variabile di istanza. La maggior parte delle proprietà è definita come retain o copy, il che significa che incrementano il conteggio di conservazione dell'oggetto quando vengono impostate, o creano una copia dell'oggetto, lasciando intatto l'originale.

Nel tuo esempio, probabilmente avete nel vostro file .h:

@property (retain) MyObject *editMyObject; 

Quindi nel tuo primo esempio:

// (2) property setter increments retain count to 2 
self.editMyObject = 

    // (1) new object created with retain count of 1 
    [[MyObject alloc] init]; 

// oops! retain count is now 2 

Quando si crea la nuova istanza di MyObject usando alloc/init, ha un conto di mantenimento di uno. Quando assegni la nuova istanza a self.editMyObject, stai effettivamente chiamando il metodo -setEditMyObject: che il compilatore crea per te quando fai il @synthesize editMyObject. Quando il compilatore vede self.editMyObject = x, lo sostituisce con [self setEditMyObject: x].

Nel secondo esempio:

MyObject *temp = [[MyObject alloc] init]; 
// (1) new object created with retain count of 1 

self.editMyObject = temp; 
// (2) equivalent to [self setEditMyObject: temp]; 
// increments retain count to 2 

[temp release]; 
// (3) decrements retain count to 1 

si tiene a tuo nuovo oggetto abbastanza a lungo per liberarlo, in modo che il conteggio di conservare è equilibrato (supponendo che si rilascia nel metodo dealloc).

Vedi anche Cocoa strategy for pointer/memory management

0

Si è convenuto e ha spiegato che il codice qui sotto non ha una perdita (supponendo @property trattenere e @synthesize per editMyObject):

//does not create memory leak 
MyObject *temp = [[MyObject alloc] init]; 
self.editMyObject = tempt; 
[temp release]; 

Domanda: è qualcosa di sbagliato con il seguente codice che non utilizza un puntatore temporaneo?

//does not create memory leak ? 
self.editMyObject = [[MyObject alloc] init]; 
[editMyObject release]; 

Per me questo sembra ok.

+0

è necessario assicurarsi nel metodo dealloc che l'oggetto sia rilasciato anche: [editMyObject release] o self.editMyObject = nulla; – jyavenard

Problemi correlati