Come funziona il pool di rilascio automatico di NSAutoreleasePool?


95

A quanto ho capito, qualsiasi cosa creata con un'allocazione , una nuova o una copia deve essere rilasciata manualmente. Per esempio:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

La mia domanda, però, è che non sarebbe altrettanto valida ?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}

Risposte:


68

Sì, il tuo secondo snippit di codice è perfettamente valido.

Ogni volta che l'autorelease viene inviato a un oggetto, viene aggiunto al pool di autorelease più interno. Quando la piscina viene svuotata, invia semplicemente -release a tutti gli oggetti nella piscina.

I pool di rilascio automatico sono semplicemente una comodità che ti consente di posticipare l'invio del rilascio a "più tardi". Quel "dopo" può verificarsi in diversi punti, ma il più comune nelle app Cocoa GUI è alla fine del ciclo di esecuzione corrente.


5
dov'è la fine del ciclo di esecuzione corrente, se non ho un ciclo?
Grazie

24
"Il più esterno" non dovrebbe essere "il più interno"?
Mike Weller

an objectdovrebbe essere an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.

1
EDIT: modificato il più esterno in più interno.
chakrit

1
Importante: se si utilizza il conteggio automatico dei riferimenti (ARC), non è possibile utilizzare direttamente i pool di rilascio automatico. Invece, usi i blocchi @autoreleasepool. Da developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman

37

NSAutoreleasePool: scarico vs rilascio

Poiché la funzione di draine releasesembra causare confusione, potrebbe valere la pena chiarire qui (sebbene questo sia trattato nella documentazione ...).

A rigor di termini, dal punto di vista del quadro generale nondrain è equivalente a :release

In un ambiente con conteggio dei riferimenti, drainesegue le stesse operazioni di release, quindi i due sono in tal senso equivalenti. Per sottolineare, questo significa che non si perde una piscina se si utilizza drainpiuttosto che release.

In un ambiente raccolto dalla spazzatura, releaseè un no-op. Quindi non ha effetto. drain, d'altra parte, contiene un suggerimento per il collezionista che dovrebbe "raccogliere se necessario". Pertanto, in un ambiente con raccolta dei rifiuti, l'utilizzo drainaiuta il sistema a bilanciare le operazioni di raccolta.


4
Fondamentalmente è impossibile "trapelare" a NSAutoreleasePool. Questo perché i pool funzionano come uno stack. La creazione di un'istanza di un pool spinge tale pool all'inizio dello stack del pool di rilascio automatico dei thread. -releasefa sì che quel pool salti dallo stack E tutti i pool che sono stati inseriti sopra di esso, ma per qualsiasi motivo non sono stati estratti.
johne

7
In che modo questo è rilevante per ciò che ho scritto?
mmalc

2
Mi piace come si è preso il tempo per audace AND. SCATTO!
Billy Grey

17

Come già sottolineato, il tuo secondo snippet di codice è corretto.

Vorrei suggerire un modo più succinto di utilizzare il pool di rilascio automatico che funziona su tutti gli ambienti (conteggio ref, GC, ARC) ed evita anche la confusione di scarico / rilascio:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

Nell'esempio sopra si prega di notare il blocco @autoreleasepool . Questo è documentato qui .


2
Si prega di notare che il rilascio automatico non è consentito con ARC.
dmirkitanov

1
Per chiarire, è necessario utilizzare il @autoreleasepoolblocco con ARC.
Simon

7

No, ti sbagli. La documentazione afferma chiaramente che sotto non-GC, -drain è equivalente a -release, il che significa che NSAutoreleasePool non sarà trapelato.


Mi chiedevo perché Xcode avrebbe generato codice con -drain se così fosse. Ho usato -drain perché pensavo fosse equivalente a -release basato sul codice generato da Xcode.
James Sumners,

1
È fondamentalmente impossibile 'trapelare' a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne


0

l'invio del rilascio automatico invece del rilascio a un oggetto estende la durata di quell'oggetto almeno fino a quando il pool stesso non viene svuotato (potrebbe essere più lungo se l'oggetto viene successivamente conservato). Un oggetto può essere inserito più volte nello stesso pool, nel qual caso riceve un messaggio di rilascio ogni volta che è stato inserito nel pool.


-2

Sì e no. Finiresti per rilasciare la memoria di stringa ma "trapelare" l'oggetto NSAutoreleasePool in memoria utilizzando drain invece di release se lo hai eseguito in un ambiente Garbage Collection (non gestito dalla memoria). Questa "perdita" rende semplicemente l'istanza di NSAutoreleasePool "irraggiungibile" come qualsiasi altro oggetto senza puntatori forti sotto GC, e l'oggetto verrebbe ripulito la prossima volta che GC viene eseguito, il che potrebbe benissimo essere direttamente dopo la chiamata a -drain:

scolare

In un ambiente Garbage Collection, attiva la Garbage Collection se la memoria allocata dall'ultima raccolta è maggiore della soglia corrente; altrimenti si comporta come release. ... In un ambiente raccolto dalla spazzatura, questo metodo alla fine chiama objc_collect_if_needed.

Altrimenti, è simile a come -releasesi comporta in condizioni non GC, sì. Come altri hanno affermato, -releaseè un no-op in GC, quindi l'unico modo per assicurarsi che il pool funzioni correttamente in GC è attraverso -drain, e -drainin non-GC funziona esattamente come -releasein non-GC e probabilmente comunica la sua funzionalità più chiaramente come bene.

Vorrei sottolineare che la tua dichiarazione "qualsiasi cosa chiamata con new, alloc o init" non dovrebbe includere "init" (ma dovrebbe includere "copy"), perché "init" non alloca memoria, imposta solo l'oggetto (costruttore moda). Se hai ricevuto un oggetto allocato e la tua funzione si chiama solo init come tale, non lo rilasceresti:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Ciò non consuma più memoria di quella con cui hai già iniziato (supponendo che init non installi gli oggetti, ma non sei comunque responsabile di quelli).


Non mi sento a mio agio nel lasciare questa risposta accettata quando le tue informazioni sullo scarico non sono corrette. Vedi developer.apple.com/documentation/Cocoa/Reference/Foundation/… Update e accetterò di nuovo.
James Sumners,

Cosa c'è di inesatto nella risposta? In un ambiente raccolto spazzatura (come detto), scarico non elimina l'AutoReleasePool, in modo da sarà una perdita di memoria a meno che non si è utilizzato il rilascio. La citazione che ho elencato era direttamente dalla bocca del cavallo, i documenti in fuga.
Loren Segal

1
Loren: In GC, - [NSAutoreleasePool drain] attiverà una raccolta. -retain, -release e -autorelease vengono tutti ignorati dal collector; ecco perché -drain viene utilizzato sui pool con rilascio automatico in GC.
Chris Hanson

Nella documentazione per 'drain': in un ambiente di memoria gestita, si comporta come chiamare release. Quindi non perderai memoria se usi "drain" invece di release.
mmalc

-[NSAutoreleasePool release] in un ambiente raccolto dai rifiuti è un no-op. -[NSAutoreleasePool drain]funziona sia in ambienti con conteggio dei riferimenti che in quelli raccolti da rifiuti.
Jonathan Sterling
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.