Domanda

Sto appena iniziando a dare un'occhiata a Objective-C e Cocoa con l'obiettivo di giocare con l'SDK di iPhone.Sono abbastanza a mio agio con i C malloc E free concetto, ma lo schema di conteggio delle referenze di Cocoa mi ha piuttosto confuso.Mi è stato detto che è molto elegante una volta capito, ma non ho ancora superato il limite.

Come faccio release, retain E autorelease funzionano e quali sono le convenzioni sul loro utilizzo?

(O in mancanza di ciò, cosa hai letto che ti ha aiutato a ottenerlo?)

È stato utile?

Soluzione

Iniziamo con retain E release; autorelease è davvero solo un caso speciale una volta compresi i concetti di base.

In Cocoa, ogni oggetto tiene traccia di quante volte viene fatto riferimento (in particolare, the NSObject la classe base lo implementa).A chiamata retain su un oggetto, gli stai dicendo che vuoi aumentare di uno il conteggio dei riferimenti.A chiamata release, dici all'oggetto che lo stai lasciando andare e il suo conteggio dei riferimenti viene diminuito.Se, dopo aver chiamato release, il conteggio dei riferimenti è ora zero, la memoria dell'oggetto viene liberata dal sistema.

Il modo di base in cui questo differisce da malloc E free è che ogni dato oggetto non deve preoccuparsi del crash di altre parti del sistema perché hai liberato la memoria che stavano utilizzando.Supponendo che tutti stiano giocando e trattenendo/rilasciando secondo le regole, quando un pezzo di codice conserva e poi rilascia l'oggetto, qualsiasi altro pezzo di codice che fa riferimento all'oggetto non sarà influenzato.

Ciò che a volte può creare confusione è conoscere le circostanze in cui dovresti chiamare retain E release.La mia regola generale è che se voglio aggrapparmi a un oggetto per un certo periodo di tempo (se è una variabile membro di una classe, per esempio), allora devo assicurarmi che il conteggio dei riferimenti dell'oggetto sappia di me.Come descritto sopra, il conteggio dei riferimenti di un oggetto viene incrementato chiamando retain.Per convenzione, viene anche incrementato (impostato a 1, in realtà) quando l'oggetto viene creato con un metodo "init".In entrambi i casi è mia responsabilità chiamare release sull'oggetto quando ho finito.In caso contrario, si verificherà una perdita di memoria.

Esempio di creazione di oggetti:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Adesso per autorelease.Il rilascio automatico viene utilizzato come modo conveniente (e talvolta necessario) per dire al sistema di liberare l'oggetto dopo un po' di tempo.Dal punto di vista idraulico, quando autorelease si chiama il thread corrente NSAutoreleasePool viene avvisato della chiamata.IL NSAutoreleasePool ora sa che una volta ottenuta un'opportunità (dopo l'attuale iterazione del ciclo degli eventi), può chiamare release sull'oggetto.Dal nostro punto di vista di programmatori, si occupa delle chiamate release per noi, quindi non dobbiamo (e in effetti non dovremmo).

Ciò che è importante notare è che (di nuovo, per convenzione) tutta la creazione di oggetti classe i metodi restituiscono un oggetto rilasciato automaticamente.Nell'esempio seguente, ad esempio, la variabile "s" ha un conteggio dei riferimenti pari a 1, ma al termine del ciclo di eventi verrà eliminata.

NSString* s = [NSString stringWithString:@"Hello World"];

Se vuoi aggrapparti a quel filo, devi chiamare retain esplicitamente e poi esplicitamente release quando hai finito.

Considera il seguente pezzo di codice (molto artificioso) e vedrai una situazione in cui autorelease è obbligatorio:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

Mi rendo conto che tutto questo crea un po' di confusione, ma a un certo punto funzionerà.Ecco alcuni riferimenti per iniziare:

  • L'introduzione di Apple alla gestione della memoria.
  • Programmazione Cocoa per Mac OS X (4a edizione), di Aaron Hillegas - un libro molto ben scritto con molti ottimi esempi.Si legge come un tutorial.
  • Se ti stai davvero immergendo, potresti andare a Grande ranch dei nerd.Questa è una struttura di formazione gestita da Aaron Hillegas, l'autore del libro sopra menzionato.Ho frequentato il corso Intro to Cocoa lì diversi anni fa ed è stato un ottimo modo per imparare.

Altri suggerimenti

Se comprendi il processo di conservazione/rilascio, allora ci sono due regole d'oro che sono "duh" ovvie per i programmatori Cocoa affermati, ma sfortunatamente raramente vengono spiegate chiaramente per i nuovi arrivati.

  1. Se una funzione che restituisce un oggetto ha alloc, create O copy nel suo nome allora l'oggetto è tuo.Devi chiamare [object release] quando hai finito.O CFRelease(object), se si tratta di un oggetto Core-Foundation.

  2. Se NON contiene una di queste parole nel nome allora l'oggetto appartiene a qualcun altro.Devi chiamare [object retain] se desideri conservare l'oggetto dopo la fine della tua funzione.

Ti sarebbe utile seguire questa convenzione anche nelle funzioni che crei tu stesso.

(Pignoli:Sì, purtroppo ci sono alcune chiamate API che costituiscono eccezioni a queste regole ma sono rare).

Se stai scrivendo codice per il desktop e puoi scegliere come target Mac OS X 10.5, dovresti almeno esaminare l'utilizzo della garbage collection Objective-C.Semplificherà davvero la maggior parte del tuo sviluppo: ecco perché Apple ha messo tutto l'impegno nel crearlo e nel farlo funzionare bene.

Per quanto riguarda le regole di gestione della memoria quando non si utilizza GC:

  • Se crei un nuovo oggetto utilizzando +alloc/+allocWithZone:, +new, -copy O -mutableCopy o se tu -retain un oggetto, ne stai assumendo la proprietà e devi assicurarti che venga inviato -release.
  • Se ricevi un oggetto in qualsiasi altro modo, lo sei non il proprietario di esso e dovrebbe non assicurati che venga inviato -release.
  • Se vuoi assicurarti che un oggetto venga inviato -release puoi inviarlo tu stesso oppure puoi inviare l'oggetto -autorelease e la corrente pool di rilascio automatico lo invierà -release (una volta per ricevuto -autorelease) quando la piscina viene svuotata.

Tipicamente -autorelease viene utilizzato come modo per garantire che gli oggetti vivano per la durata dell'evento corrente, ma vengano ripuliti in seguito, poiché esiste un pool di rilascio automatico che circonda l'elaborazione degli eventi di Cocoa.Nel Cacao, lo è lontano è più comune restituire a un chiamante oggetti che vengono rilasciati automaticamente piuttosto che restituire oggetti che il chiamante stesso deve rilasciare.

Usi Objective-C Conteggio dei riferimenti, il che significa che ogni oggetto ha un conteggio dei riferimenti.Quando viene creato un oggetto, ha un conteggio dei riferimenti pari a "1".In parole povere, quando si fa riferimento a un oggetto (cioè, viene archiviato da qualche parte), viene "trattenuto", il che significa che il suo conteggio dei riferimenti viene aumentato di uno.Quando un oggetto non è più necessario, viene "rilasciato", il che significa che il suo conteggio dei riferimenti viene diminuito di uno.

Quando il conteggio dei riferimenti di un oggetto è 0, l'oggetto viene liberato.Questo è il conteggio dei riferimenti di base.

Per alcune lingue, i riferimenti vengono automaticamente aumentati e diminuiti, ma Objective-c non è una di quelle lingue.Pertanto il programmatore è responsabile del mantenimento e del rilascio.

Un modo tipico di scrivere un metodo è:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

Il problema di dover ricordarsi di rilasciare eventuali risorse acquisite all'interno del codice è noioso e soggetto a errori.Objective-C introduce un altro concetto volto a rendere tutto ciò molto più semplice:Pool di rilascio automatico.I pool di rilascio automatico sono oggetti speciali installati su ciascun thread.Sono una classe abbastanza semplice, se cerchi NSAutoreleasePool.

Quando un oggetto riceve un messaggio di "rilascio automatico", l'oggetto cercherà eventuali pool di rilascio automatico presenti nello stack per questo thread corrente.Aggiungerà l'oggetto all'elenco come oggetto a cui inviare un messaggio di "rilascio" in un momento futuro, che generalmente avviene quando il pool stesso viene rilasciato.

Prendendo il codice qui sopra, puoi riscriverlo per renderlo più breve e più facile da leggere dicendo:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Poiché l'oggetto viene rilasciato automaticamente, non è più necessario chiamare esplicitamente "release" su di esso.Questo perché sappiamo che alcuni pool di rilascio automatico lo faranno per noi in seguito.

Spero che questo aiuti.L'articolo di Wikipedia è abbastanza buono riguardo al conteggio dei riferimenti.Maggiori informazioni su i pool di rilascio automatico possono essere trovati qui.Tieni inoltre presente che se stai creando per Mac OS X 10.5 e versioni successive, puoi dire a Xcode di compilare con la garbage collection abilitata, consentendoti di ignorare completamente il mantenimento/rilascio/rilascio automatico.

Joshua (#6591) - Il materiale della Garbage Collection in Mac OS X 10.5 sembra piuttosto interessante, ma non è disponibile per iPhone (o se vuoi che la tua app funzioni su versioni precedenti alla 10.5 di Mac OS X).

Inoltre, se stai scrivendo una libreria o qualcosa che potrebbe essere riutilizzato, l'uso della modalità GC blocca chiunque utilizzi il codice a utilizzare anche la modalità GC, quindi, a quanto ho capito, chiunque cerchi di scrivere codice ampiamente riutilizzabile tende a gestire memoria manualmente.

Come sempre, quando le persone iniziano a riformulare il materiale di riferimento, quasi invariabilmente sbagliano qualcosa o forniscono una descrizione incompleta.

Apple fornisce una descrizione completa del sistema di gestione della memoria di Cocoa nel file Guida alla programmazione della gestione della memoria per Cocoa, al termine del quale si trova un breve ma accurato riassunto dell'opera Regole di gestione della memoria.

Non aggiungerò allo specifico di mantenimento/rilascio altro che potresti voler pensare di spendere $ 50 e ottenere il libro Hillegass, ma suggerirei caldamente di iniziare a utilizzare gli strumenti Instruments molto presto nello sviluppo della tua applicazione (anche il tuo il primo!).Per fare ciò, Esegui->Inizia con gli strumenti di prestazione.Inizierei con Leaks che è solo uno dei tanti strumenti disponibili ma ti aiuterà a mostrarti quando ti sei dimenticato di pubblicare.È davvero scoraggiante la quantità di informazioni che ti verranno presentate.Ma dai un'occhiata a questo tutorial per iniziare velocemente:
TUTORIAL SUL CACAO:RISOLVERE LE PERDITE DI MEMORIA CON GLI STRUMENTI

In realtà ci sto provando forza le perdite potrebbero essere un modo migliore per imparare a prevenirle!Buona fortuna ;)

Matt Dillard ha scritto:

return [[s rilascio automatico] rilascio];

Il rilascio automatico lo fa non conservare l'oggetto.Il rilascio automatico lo mette semplicemente in coda per essere rilasciato in seguito.Non vuoi avere una dichiarazione di rilascio lì.

La mia solita raccolta di articoli sulla gestione della memoria di Cocoa:

gestione della memoria del cacao

È disponibile uno screencast gratuito dalla rete iDeveloperTV

Gestione della memoria in Objective-C

La risposta di NilObject è un buon inizio.Ecco alcune informazioni supplementari relative alla gestione manuale della memoria (richiesto sull'iPhone).

Se tu personalmente alloc/init un oggetto, viene fornito con un conteggio dei riferimenti pari a 1.Sei responsabile della pulizia successiva quando non è più necessaria, anche chiamando [foo release] O [foo autorelease].release lo ripulisce immediatamente, mentre autorelease aggiunge l'oggetto al pool di autorelease, che lo rilascerà automaticamente in un secondo momento.

il rilascio automatico è principalmente utile quando si dispone di un metodo che deve restituire l'oggetto in questione (quindi non puoi rilasciarlo manualmente, altrimenti restituirai un oggetto nil) ma non vuoi nemmeno trattenertelo.

Se acquisisci un oggetto per cui non hai chiamato alloc/init per ottenerlo, ad esempio:

foo = [NSString stringWithString:@"hello"];

ma vuoi mantenere questo oggetto, devi chiamare [foo keep].Altrimenti, è possibile che ottenga autoreleased e rimarrai aggrappato a un riferimento pari a zero (come nel caso sopra stringWithString esempio).Quando non ti serve più, chiama [foo release].

Le risposte di cui sopra forniscono chiare riformulazioni di ciò che dice la documentazione;il problema che incontra la maggior parte delle nuove persone sono i casi non documentati.Per esempio:

  • Rilascio automatico:I documenti dicono che innescherà una versione "ad un certo punto in futuro". QUANDO?!Fondamentalmente, puoi contare sul fatto che l'oggetto sia in giro finché non esci nuovamente dal codice nel ciclo degli eventi di sistema.Il sistema PUÒ rilasciare l'oggetto in qualsiasi momento dopo il ciclo di eventi corrente.(Penso che Matt lo abbia detto prima.)

  • Stringhe statiche: NSString *foo = @"bar"; -- devi conservarlo o rilasciarlo?NO.Che ne dite di

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • La regola della creazione:Se lo hai creato, ne sei il proprietario e ci si aspetta che lo rilasci.

In generale, il modo in cui i nuovi programmatori Cocoa si confondono è non capire quali routine restituiscono un oggetto con a retainCount > 0.

Ecco un frammento di Regole molto semplici per la gestione della memoria nel cacao:

Regole del conteggio di conservazione

  • All'interno di un dato blocco, l'uso di -copy, -alloc e -retain dovrebbe essere uguale all'uso di -release e -autorelease.
  • Oggetti creati utilizzando costruttori di convenienza (ad es.stringWithString di NSString) sono considerati rilasciati automaticamente.
  • Implementa un metodo -dealloc per rilasciare le variabili di istanza di tua proprietà

Il primo punto dice:se hai chiamato alloc (O new fooCopy), è necessario chiamare release su quell'oggetto.

Il 2° punto dice:se usi un costruttore di convenienza e hai bisogno dell'oggetto per restare in giro (come con un'immagine da disegnare in seguito), è necessario conservarla (e poi rilasciarla in seguito).

Il 3° dovrebbe essere autoesplicativo.

Molte buone informazioni anche su Cocoadev:

Come già menzionato da molti, quello di Apple Introduzione alla gestione della memoria è di gran lunga il posto migliore per iniziare.

Un collegamento utile che non ho ancora visto menzionato è Gestione pratica della memoria.Lo troverai nel mezzo dei documenti di Apple se li leggi, ma vale la pena collegarlo direttamente.È un brillante riepilogo esecutivo delle regole di gestione della memoria con esempi ed errori comuni (sostanzialmente ciò che altre risposte qui stanno cercando di spiegare, ma non altrettanto bene).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top