Wie funktioniert der NSAutoreleasePool-Autorelease-Pool?

95

Soweit ich weiß, muss alles, was mit einer Zuordnung , einer neuen oder einer Kopie erstellt wurde , manuell freigegeben werden. Beispielsweise:

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

Meine Frage ist jedoch, ob dies nicht genauso gültig ist?:

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

Antworten:

68

Ja, Ihr zweiter Code-Snippit ist vollkommen gültig.

Jedes Mal, wenn -autorelease an ein Objekt gesendet wird, wird es dem innersten Autorelease-Pool hinzugefügt. Wenn der Pool geleert wird, sendet er einfach -release an alle Objekte im Pool.

Autorelease-Pools sind lediglich eine Annehmlichkeit, mit der Sie das Senden von Releases bis "später" verschieben können. Das "später" kann an mehreren Stellen passieren, aber die häufigste in Cocoa GUI-Apps ist am Ende des aktuellen Run-Loop-Zyklus.

kperryua
quelle
5
Wo ist das Ende des aktuellen Run-Loop-Zyklus, wenn ich keine Loop habe?
Danke
24
Sollte "äußerste" nicht "innerste" sein?
Mike Weller
an objectsollte sein an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.
1
BEARBEITEN: Von außen nach innen geändert.
Chakrit
1
Wichtig: Wenn Sie die automatische Referenzzählung (ARC) verwenden, können Sie Autorelease-Pools nicht direkt verwenden. Stattdessen verwenden Sie @ autoreleasepool-Blöcke. Von developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman
37

NSAutoreleasePool: Drain vs. Release

Da die Funktion von drainund releaseVerwirrung zu stiften scheint, kann es hier sinnvoll sein, dies zu klären (obwohl dies in der Dokumentation behandelt wird ...).

Streng genommen, aus der großen Bild Perspektive drainist nicht gleichbedeutend mit release:

Führt in einer Umgebung mit Referenzzählung draindieselben Operationen aus wie release, sodass die beiden in diesem Sinne äquivalent sind. Um zu betonen, Das heißt , Sie nicht undicht einen Pool , wenn Sie verwenden drainstatt release.

In einer Müllsammelumgebung releaseist ein No-Op. Somit hat es keine Wirkung. drainenthält andererseits einen Hinweis an den Sammler, dass er "bei Bedarf sammeln" sollte. In einer Umgebung, in der Müll gesammelt wird, drainhilft die Verwendung des Systems dabei, das Sammeln von Sammlungen auszugleichen.

mmalc
quelle
4
Es ist grundsätzlich unmöglich, a zu "lecken" NSAutoreleasePool. Dies liegt daran, dass Pools wie ein Stapel funktionieren. Durch das Instanziieren eines Pools wird dieser Pool an die Spitze des Autorelease-Poolstapels des Threads verschoben. -releasebewirkt, dass dieser Pool aus dem Stapel herausspringt UND alle Pools, die darauf geschoben wurden, aber aus irgendeinem Grund nicht aufgetaucht sind.
Johne
7
Inwiefern ist das relevant für das, was ich geschrieben habe?
Mmalc
2
Mir gefällt, wie er sich die Zeit genommen hat, UND zu kühnen. SNAP!
Billy Gray
17

Wie bereits erwähnt, ist Ihr zweites Code-Snippet korrekt.

Ich möchte eine prägnantere Methode für die Verwendung des Autorelease-Pools vorschlagen, die in allen Umgebungen (Ref Counting, GC, ARC) funktioniert und auch die Verwirrung zwischen Drain und Release vermeidet:

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

Beachten Sie im obigen Beispiel den @ autoreleasepool- Block. Dies ist hier dokumentiert .

Neovibrant
quelle
2
Bitte beachten Sie, dass eine Autorelease mit ARC nicht zulässig ist.
Dmirkitanov
1
Zur Verdeutlichung muss man den @autoreleasepoolBlock mit ARC verwenden.
Simon
7

Nein, du liegst falsch. In der Dokumentation wird klargestellt, dass -drain unter Nicht-GC gleichbedeutend mit -release ist, was bedeutet, dass der NSAutoreleasePool nicht durchgesickert ist.

kperryua
quelle
Ich fragte mich, warum Xcode Code mit -drain generieren würde, wenn dies der Fall wäre. Ich habe -drain verwendet, weil ich dachte, es wäre gleichbedeutend mit -release basierend auf dem von Xcode generierten Code.
James Sumners
1
Es ist grundsätzlich unmöglich, NSAutoreleasePool
Folgendes
0

Das Senden einer Autorelease anstelle einer Freigabe an ein Objekt verlängert die Lebensdauer dieses Objekts mindestens bis zum Entleeren des Pools selbst (es kann länger sein, wenn das Objekt anschließend beibehalten wird). Ein Objekt kann mehrmals in denselben Pool gestellt werden. In diesem Fall erhält es für jedes Mal, wenn es in den Pool gelegt wurde, eine Freigabemeldung.

Hardik Mamtora
quelle
-2

Ja und nein. Sie würden am Ende den String-Speicher freigeben, aber das NSAutoreleasePool-Objekt in den Speicher "verlieren", indem Sie Drain anstelle von Release verwenden, wenn Sie dies in einer durch Müll gesammelten (nicht speicherverwalteten) Umgebung ausführen. Dieses "Leck" macht die Instanz von NSAutoreleasePool einfach "nicht erreichbar" wie jedes andere Objekt ohne starke Zeiger unter GC, und das Objekt würde beim nächsten Ausführen von GC bereinigt, was sehr wohl direkt nach dem Aufruf von -drain:

ablassen

Löst in einer Speicherbereinigungsumgebung die Speicherbereinigung aus, wenn der seit der letzten Erfassung zugewiesene Speicher größer als der aktuelle Schwellenwert ist. verhält sich ansonsten wie Release. ... In einer Müllsammelumgebung ruft diese Methode letztendlich auf objc_collect_if_needed.

Ansonsten ist es ähnlich wie -releasebei Nicht-GC, ja. Wie andere angegeben haben, -releasehandelt es sich bei GC um ein No-Op. Die einzige Möglichkeit, um sicherzustellen, dass der Pool unter GC ordnungsgemäß funktioniert, besteht darin -drain, und -drainunter Nicht-GC funktioniert genau wie -releaseunter Nicht-GC und kommuniziert seine Funktionalität möglicherweise klarer als Gut.

Ich sollte darauf hinweisen, dass Ihre Anweisung "alles, was mit new, alloc oder init aufgerufen wird" nicht "init" enthalten sollte (sondern "copy"), da "init" keinen Speicher zuweist, sondern nur das Objekt (Konstruktor) einrichtet Mode). Wenn Sie ein zugewiesenes Objekt erhalten und Ihre Funktion nur init als solches aufgerufen hätte, würden Sie es nicht freigeben:

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

Das verbraucht nicht mehr Speicher als Sie bereits begonnen haben (vorausgesetzt, init instanziiert keine Objekte, aber Sie sind trotzdem nicht dafür verantwortlich).

Loren Segal
quelle
Ich fühle mich nicht wohl, wenn ich diese Antwort als akzeptiert lasse, wenn Ihre Informationen über Abfluss nicht ganz richtig sind. Siehe developer.apple.com/documentation/Cocoa/Reference/Foundation/… Update und ich werde es erneut akzeptieren.
James Sumners
Was ist an der Antwort ungenau? In einer Müll gesammelt Umgebung (wie bereits erwähnt), abtropfen lassen löscht nicht die AutoReleasePool, so dass Sie werden ein Speicherleck auf, wenn Sie Freigabe verwendet. Das Zitat, das ich aufgelistet habe, war direkt aus dem Maul des Pferdes, die Dokumente im Abfluss.
Loren Segal
1
Loren: Unter GC löst - [NSAutoreleasePool Drain] eine Sammlung aus. -retain, -release und -autorelease werden vom Sammler ignoriert. Aus diesem Grund wird -drain für Autorelease-Pools unter GC verwendet.
Chris Hanson
In der Dokumentation zu 'Drain': In einer verwalteten Speicherumgebung verhält sich dies genauso wie das Aufrufen von release. Somit verlieren Sie keinen Speicher, wenn Sie "Drain" anstelle von "Release" verwenden.
Mmalc
-[NSAutoreleasePool release]In einer Müllsammelumgebung ist ein No-Op. -[NSAutoreleasePool drain]funktioniert sowohl in referenzgezählten als auch in müllsammelten Umgebungen.
Jonathan Sterling