2009-07-18 4 views
5

Ich habe mir die Fragen zu StackOverflow angeschaut, aber es gibt so viele Fragen zum Speichermanagement in Objective-C, dass ich die Antwort nicht finden konnte, nach der ich suchte.Verwenden Sie die automatische Freigabe, bevor Sie Objekte zu einer Sammlung hinzufügen?

Die Frage ist, ob es in Ordnung ist (und empfohlen), Autorelease vor dem Hinzufügen eines neu erstellten Objekts zu einer Sammlung (wie NSMutableArray) aufzurufen? Oder sollte ich es explizit nach dem Hinzufügen freigeben. (Ich weiß, NSMutableArray das Objekt behalten willl)

Das zeigt meine Frage:

Szenario A (Autorelease):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[[MyClass alloc] init] autorelease]; 

    [array addObject:obj]; 
} 

Szenario B (explizite Freigabe):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[MyClass alloc] init]; 

    [array addObject:obj]; 

    [obj release]; 
} 

Ich nehme an, beide sind richtig, aber ich bin mir nicht sicher, und ich weiß nicht, was der bevorzugte Weg ist.

Können die Objective-C-Gurus etwas Licht ins Dunkel bringen?

Antwort

7

Beide sind korrekt und funktionieren so, wie Sie es erwarten.

Ich persönlich bevorzuge es, die letztere Methode zu verwenden, aber nur, weil ich es gerne ausdrücke, wenn Objekte freigegeben werden. Indem wir das Objekt automatisch freigeben, sagen wir nur: "Dieses Objekt wird zu einem beliebigen Zeitpunkt in der Zukunft freigesetzt." Das bedeutet, dass Sie das Autoreleased-Objekt in das Array setzen, das Array zerstören können und das Objekt (wahrscheinlich) noch existieren kann.

Bei der letztgenannten Methode würde das Objekt sofort mit dem Array zerstört werden (sofern nichts anderes dazwischengekommen ist und es in der Zwischenzeit behalten hat). Wenn ich mich in einer Umgebung mit eingeschränktem Speicher befinde (z. B. dem iPhone), bei der ich darauf achten muss, wie viel Speicher ich verwende, verwende ich die letztere Methode, damit ich nicht so viele Objekte habe ein NSAutoreleasePool irgendwo. Wenn die Speicherbelegung für Sie keine große Rolle spielt (und normalerweise auch nicht für mich), dann ist jede Methode völlig akzeptabel.

+2

Wenn Sie möchten, dass Ihr Code absolut sicher ist, wird dringend empfohlen, die Autorelease zu verwenden: Siehe http://stackoverflow.com/questions/1147785/use-autorelease-before-adding-objects-to-a-collection/1149040#1149040 – Casebash

3

Sie sind beide korrekt, aber B kann bevorzugt werden, da es überhaupt keinen Overhead hat. Autorelease bewirkt, dass der Autorelease-Pool die Verantwortung für das Objekt übernimmt. Dies hat einen sehr geringen Overhead, der natürlich mit der Anzahl der beteiligten Objekte multipliziert wird.

Also mit einem Objekt A und B sind mehr oder weniger die gleichen, aber definitiv nicht verwenden A in Szenarien mit vielen Objekten zum Array hinzufügen.

In verschiedenen Situationen kann Autoreleasing die Freigabe vieler Objekte am Ende des Threads verzögern und ansammeln. Dies kann suboptimal sein. Achten Sie darauf, dass Autoreleasing häufig ohne explizite Intervention geschieht. Zum Beispiel sind viele Getter auf diese Weise umgesetzt:

return [[myObject retain] autorelease]; 

so, wenn Sie die Getter rufen fügen Sie ein Objekt in den Autofreigabepool.

+0

„Autorelease bewirkt, dass der Autofreigabepool Ladung des Objekts zu nehmen. " Dies ist falsch. Der Autorelease-Pool ist nichts anderes als eine verzögerte Nachrichtenübermittlungseinrichtung. Der Autorelease-Pool übernimmt kein Objekt, sondern listet ihn nur auf, um in der Zukunft eine Freigabemeldung zu erhalten. – NSResponder

+0

Mit "übernehmen" meinte ich genau das. Ich bin in der EU geboren, aber nicht in Großbritannien, meine Schuld. – IlDan

0

Sie haben alloc ‚ed das Objekt, dann Ihre Aufgabe es ist es irgendwann zu veröffentlichen. Beide Code-Schnipsel funktionieren nur auf die gleiche, korrekte Art und Weise, wobei die autorelease Art das potentiell langsamere Gegenstück ist.

Persönlich gesprochen, bevorzuge ich die autorelease Weise, da es einfach zu tippen ist und fast nie ist ein Engpass.

0

Sie sind beide in Ordnung. Einige Leute werden Ihnen sagen, dass Sie die Autorelease wegen "Overhead" oder so etwas vermeiden sollen, aber die Wahrheit ist, dass es praktisch keinen Overhead gibt. Probieren Sie es aus und versuchen Sie den "Overhead" zu finden. Der einzige Grund, warum du es vermeiden würdest, ist in einer Situation wie in deinem Gedächtnis, in der du gehungert wirst, wie auf dem iPhone. Unter OS X haben Sie praktisch unbegrenzten Speicher, so dass es keinen großen Unterschied machen wird. Verwenden Sie einfach das, was für Sie am bequemsten ist.

+1

Es ist nicht so geschnitten und so getrocknet. Wenn Objekte in einer Schleife automatisch freigegeben werden, ist es möglich, dass eine große Anzahl von zusätzlichen Objekten übrig bleibt, bis ein umschließender Autorelease-Pool leer ist. Dies kann manchmal zu erheblichen Speicher-Overhead und Verlangsamungen führen. (Ja, es ist mir passiert.) Dies ist jedoch nicht so häufig. Normalerweise können Sie das Problem ignorieren, bis es passiert, und dann schauen Sie sich an, was in der Schleife vor sich geht und optimieren Sie es mit expliziten Freigaben. –

+1

Alles, was erfordert, ist eine spezielle Verwaltung des Autorelease-Pools. Es bedeutet immer noch nicht, dass Autorelease eine schlechte Sache ist, besonders nicht im allgemeinen Fall. – Chuck

11

IMHO, welcher Weg ist "richtig" ist eine Frage der Präferenz. Ich stimme den Respondern nicht zu, die befürworten, autorelease nicht zu verwenden, aber meine Präferenz ist, autorelease zu verwenden, es sei denn, es gibt einen überwältigend zwingenden Grund nicht zu. Ich werde meine Gründe aufzählen und Sie können entscheiden, ob sie zu Ihrem Stil der Programmierung passen oder nicht.

Wie Chuck herausfand, gibt es eine semi-urbane Legende, dass es einen gewissen Overhead für die Verwendung von Autorelease-Pools gibt. Dies könnte nicht weiter von der Wahrheit entfernt sein, und das kommt von unzähligen Stunden mit Shark.app, um das letzte bisschen Leistung aus dem Code zu drücken. Der Versuch, dies zu optimieren, liegt im Bereich der "voreiligen Optimierung". Wenn und nur wenn, Shark.app Ihnen harte Daten gibt, dass dies ein Problem sein könnte, sollten Sie sogar in Betracht ziehen, darauf zu schauen.

Wie andere darauf hingewiesen haben, wird ein automatisch freigegebenes Objekt zu einem späteren Zeitpunkt "freigegeben". Das heißt, sie verweilen und nehmen das Gedächtnis auf, bis dieser "spätere Punkt" herumrollt. Für "die meisten" Fälle befindet sich dies am Ende eines Ereignisverarbeitungsdurchlaufs, bevor die Ausführungsschleife bis zum nächsten Ereignis ruht (Zeitgeber, Benutzer, der auf etwas klickt usw.).

Gelegentlich müssen Sie diese temporären Objekte eher früher als später entfernen. Beispielsweise müssen Sie eine riesige Datei mit mehreren Megabyte oder mehrere zehntausend Zeilen aus einer Datenbank verarbeiten. Wenn dies passiert, müssen Sie einen an einem gut gewählten Punkt, gefolgt von einem [pool release]; an der Unterseite platzieren. Dies geschieht fast immer in einer Art "Schleifen-Batch-Verarbeitung", so dass es normalerweise am Anfang und Ende einer kritischen Schleife liegt. Auch dies sollte evidenzbasiert und nicht auf Mutmaßungen basieren. In der ObjectAlloc von Instrument.app finden Sie diese Problemstellen.

Der Hauptgrund, warum ich autorelease zu release, bevorzugen allerdings, dass es viel einfacher ist, leckagefreie Programme zu schreiben. Kurz gesagt, wenn Sie den release Weg gehen wählen, müssen Sie Garantie dass release schließlich zu obj gesendet wird, unter alle Umständen. Während dies scheint einfach zu sein, ist es tatsächlich überraschend schwer in der Praxis zu tun. Nehmen Sie sich beispielsweise zum Beispiel:

// array is an instance of NSMutableArray 
    MyClass *obj = [[MyClass alloc] init]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 
    [obj release]; 

Nun, da sich vorstellen, aus irgendeinem Grund, etwas, irgendwo verletzt subtil Ihre Annahme, dass array wandelbar ist, vielleicht als Ergebnis eine Methode unter Verwendung der Ergebnisse zu verarbeiten, und die zurück Array mit den verarbeiteten Ergebnissen wurde als NSArray erstellt.Wenn Sie an das unveränderliche NSArray senden, wird eine Ausnahme ausgelöst, und Sie senden nie obj seine release Nachricht. Oder vielleicht etwas schief geht irgendwo zwischen wenn obj waren alloc d und die erforderlich Aufruf release, wie Sie eine Bedingung überprüfen und return() sofort versehentlich, weil es den Geist schlüpfte, dass dieser Anruf zu release später muss stattfinden.

Sie haben gerade ein Objekt durchgesickert. Und wahrscheinlich haben Sie sich mehrere Tage damit beschäftigt, herauszufinden, wo und warum Sie es verlieren. Aus Erfahrung werden Sie viele Stunden damit verbringen, diesen Code oben zu betrachten, in der Überzeugung, dass es nicht die Quelle des Lecks sein kann, weil Sie sehr deutlich obj eine release senden. Dann, nach einigen Tagen, werden Sie erleben, was man nur als religiöse Epiphanie bezeichnen kann, wenn Sie für die Ursache des Problems erleuchtet sind.

Betrachten Sie den autorelease Fall:

// array is an instance of NSMutableArray 
    MyClass *obj = [[[MyClass alloc] init] autorelease]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 

Jetzt ist es nicht mehr wichtig, was passiert, weil es praktisch unmöglich ist, obj versehentlich auslaufen, auch unter extrem ungewöhnlichen oder außergewöhnlichen Fällen Ecke.

+0

Das kam in meiner neuen Frage hier auf: http://stackoverflow.com/questions/2911678. Großer Punkt. –

+1

'[Pool drain]' scheint die bessere Wahl in dem Fall zu sein, dass iOS jemals GC bekommt –

0

Ich bevorzuge A (Autoreleasing) für die Kürze und "Sicherheit", wie johne es nennt. Es vereinfacht meinen Code und ich habe nie Probleme damit gehabt.

Das heißt, bis heute: Ich hatte ein Problem mit der automatischen Freigabe eines Blocks vor dem Hinzufügen zu einem Array. Siehe meine Stackoverflow Frage: [myArray addObject:[[objcBlock copy] autorelease]] crashes on dealloc'ing the array (Update: Stellt sich heraus, das Problem an anderer Stelle in meinem Code, aber noch gab es einen feinen Unterschied im Verhalten mit Autorelease ...)

Verwandte Themen