2009-08-02 16 views
25

Sono nuovo al codice gestito in memoria ma ottengo l'idea abbastanza bene.Rilascio obiettivo, autorelease e tipi di dati

Accedendo alla mia app attraverso lo strumento di perdite in XCode, ho notato che dovevo solo pulire i miei oggetti personalizzati, ma non gli array creati dinamicamente, ad esempio, quindi ho pensato che quei tipi di dati sono autorelocati - ha senso dal momento che avevo solo per rilasciare gli array che ho usato come proprietà che avevano (conservarli) su di essi.

Poi ho notato qualcosa di strano: mi è stato sempre una perdita su una certa gamma inizializzato in questo modo:

NSMutableArray *removals = [NSMutableArray new]; 

ma non uno simile

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9]; 

Ora, la ragione per uno è stato istituito con "new" è che potrebbe contenere da 0 a 99 elementi, mentre l'altro che conoscevo sarebbe stato sempre 9. Dato che entrambi gli array passano allo stesso metodo in seguito in base all'interazione dell'utente, stavo ottenendo una perdita se non ho rilasciato alla fine del metodo, o un'eccezione se l'avessi fatto!

ho cambiato il primo array di

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

e ottengo perdite e non hanno nulla a rilasciare. Qualcuno può spiegare?

risposta

65

Come indicato nella memory management rules, ogni volta che avete un oggetto che è stato creato con +alloc, +new, -copy o -mutableCopy, è proprietario e responsabile per il rilascio ad un certo punto. (Infatti, +new è solo una scorciatoia per [[MyClass alloc] init].) Come si è notato, la creazione di un array tramite [NSArray new] senza rilasciarlo è una perdita di memoria.Tuttavia, se gestisci correttamente questo oggetto, di solito è possibile rilasciarlo a un certo punto. Per esempio:

  • Se il metodo che utilizza l'array è chiamato da all'interno il metodo che crea l'array, allora si dovrebbe essere in grado di rilasciare la matrice dopo che è stato utilizzato. Se il metodo interno deve mantenere un riferimento più permanente all'array attorno, allora tale metodo è responsabile dell'invio di -retain e, eventualmente, -release all'oggetto. Ad esempio:

    - (void)myMethod { 
        NSArray *removals = [NSArray new]; 
        // ... 
        [someObject someOtherMethod:removals]; 
        [removals release]; 
    } 
    
  • Se ha creato la matrice in un metodo-init per un oggetto, il metodo-dealloc può rilasciarlo quando l'oggetto viene distrutto.

  • Se è necessario creare l'array e quindi restituire dal metodo, si è scoperto il motivo per cui è stato inventato l'autoreleasing. Il chiamante del metodo non è responsabile del rilascio dell'oggetto, poiché non è un metodo +alloc, +new, -copy o -mutableCopy, ma è necessario assicurarsi che venga rilasciato alla fine. In questo caso, si chiama manualmente -autorelease sull'oggetto prima di restituirlo. Per esempio:

    - (NSArray *)myMethod { 
        NSArray *removals = [NSArray new]; 
        // ... 
        return [removals autorelease]; 
    } 
    

Quando si crea l'array tramite +arrayWithCapacity:, non sta chiamando uno dei metodi "speciali", in modo da non si deve rilasciare il risultato. Questo è probabilmente implementato con -autorelease, molto simile all'ultimo esempio sopra, ma non necessariamente. (Per inciso, è anche possibile creare un NSMutableArray con autorizzazione automatica vuota con [NSMutableArray array], il metodo si trova in NSArray, quindi non verrà visualizzato nella documentazione sotto NSMutableArray, ma creerà un array mutabile quando inviato alla classe NSMutableArray.) Se stai per restituire l'array dal tuo metodo, puoi usarlo come abbreviazione di [[[NSMutableArray alloc] init] autorelease], ma è solo una scorciatoia. In molte situazioni, tuttavia, è possibile creare un oggetto con -init o +new e rilasciarlo manualmente al momento opportuno.

4

Il cacao utilizza alcune convenzioni di denominazione. Tutto ciò che inizia con alloc, new o copy restituisce qualcosa con un retainCount di 1 e sei tenuto a rilasciare. Qualsiasi altra cosa che restituisce una funzione ha un retainCount bilanciato (potrebbe essere trattenuto da qualcos'altro, o potrebbe essere mantenuto e rilasciato).

Quindi:

NSMutableArray *removals = [NSMutableArray new]; 

Ha un retainCount di 1, e:

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

o

NSMutableArray *removals = [NSMutableArray array]; 

Non poiché i metodi non sono prefissati con alloc, nuovo o copia. Questo è tutto spiegato nella gestione della memoria documentation. In particolare:

Si prende la proprietà di un oggetto se si create impiegando un metodo il cui nome inizia con “alloc” o “nuovi” o contiene “copia” (ad esempio, alloc, newObject, o mutableCopy) o se si invia un messaggio di ritenzione allo . Sei il responsabile della cessione della proprietà di oggetti che possiedi utilizzando la release o di autorelease. In qualsiasi altro momento si riceve un oggetto, non è necessario rilasciarlo .

+1

** Tutti ** di questi metodi restituiscono un oggetto con un conteggio di ritenzione di 1. La differenza è solo quella con alcuni dei proprietari dell'oggetto e quindi è necessario rilasciarlo e con gli altri non si possiede il oggetto e non sono obbligati a rilasciarlo (ma non può contare sul fatto che si trovi oltre la catena di chiamate corrente). – Chuck

+2

Strettamente parlando, no, non è un dettaglio di implementazione. In un certo numero di casi restituiscono le cose con diversi conti di mantenimento. Ad esempio = [UIImage imageNamed:] potrebbe restituire qualcosa con un retainCount di grandi dimensioni molto grande perché potrebbe riutilizzare un'immagine memorizzata nella cache. –

+1

Beh, sì, il conteggio dei ritiri è di per sé un dettaglio di implementazione. I documenti di Apple dicono altrettanto. E in tutti i casi elencati sopra, sulle attuali versioni di OS X, il valore di questo dettaglio di implementazione è 1. – Chuck

7

Si tratta di come le cose messe in atto dietro le quinte:

+(NSMutableArray*) new 
{ 
    return [[NSMutableArray alloc] init]; 
} 

e

+(NSMutableArray*) arrayWithCapacity:(NSNumber)capacity 
{ 
    return [[NSMutableArray alloc] initWithCapacity:capacity] **autorelease**]; 
} 

Nel primo caso l'array è allocato solo e tu sei responsabile per la de-allocazione. Al contrario, arrayWithCapacity ha un'autelease per te e non causerà perdite anche se ti dimentichi di deallocare.