2009-08-02 6 views
25

Ich bin neu in Speicher verwaltetem Code, aber ich verstehe die Idee ziemlich gut.Objective-C-Version, Autorelease und Datentypen

Als ich meine App durch das Leaks-Tool in XCode nahm, bemerkte ich, dass ich nur meine benutzerdefinierten Objekte aufräumen musste, aber nicht dynamisch erstellte Arrays, also dachte ich mir, dass diese Datentypen automatisch freigegeben sind - sinnvoll, da ich nur hatte um die Arrays freizugeben, die ich als Eigenschaften verwendet habe, die auf ihnen (behalten) hatten.

Dann bemerkte ich etwas Eigenartiges:

NSMutableArray *removals = [NSMutableArray new]; 

aber nicht einen ähnlichen

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9]; 

Jetzt einrichten, der Grund, warum man war: Ich war ein Leck an einem bestimmten Array wie folgt initialisiert bekommen mit "neu" ist, dass es 0 bis 99 Elemente enthalten könnte, während das andere, das ich kannte, immer 9 sein würde. Da beide Arrays später auf der Grundlage der Benutzerinteraktion an dieselbe Methode übergeben werden, bekam ich entweder ein Leck wenn ich nicht am Ende der Methode freigelassen habe, oder eine Ausnahme, wenn ich es getan habe!

änderte ich das erste Array

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

und ich bekomme keine Lecks und müssen nicht alles lösen. Kann mir jemand erklären?

Antwort

65

Wie in den memory management rules erwähnt, wann immer Sie ein Objekt, das Sie mit +alloc erstellt haben, +new, -copy oder -mutableCopy besitzen Sie es und sind verantwortlich für die es an einem gewissen Punkt freigibt. (In der Tat, +new ist nur Kurzschrift für [[MyClass alloc] init].) Wie Sie bereits erwähnt, ein Array über [NSArray new] erstellen, ohne es zu veröffentlichen ist ein Speicherleck.Wenn Sie jedoch mit diesem Objekt richtig umgehen, ist es normalerweise möglich, es irgendwann freizugeben. Zum Beispiel:

  • Wenn die Methode, die verwendet das Array von innerhalb die Methode aufgerufen wird, die das Array erstellt, dann sollten Sie in der Lage sein, das Array freizugeben, nachdem es verwendet worden ist. Wenn die innere Methode eine permanentere Referenz auf das Array herum haben muss, ist diese Methode dafür verantwortlich, -retain und schließlich -release an das Objekt zu senden. Zum Beispiel:

    - (void)myMethod { 
        NSArray *removals = [NSArray new]; 
        // ... 
        [someObject someOtherMethod:removals]; 
        [removals release]; 
    } 
    
  • Wenn Sie das Array in einer -init Methode für ein Objekt erstellt, dann ist die -dealloc Methode kann freigesetzt werden, wenn das Objekt zerstört wird.

  • Wenn Sie das Array erstellen müssen und dann zurückgeben es von der Methode, haben Sie den Grund entdeckt, dass Autoreleasing erfunden wurde. Der Aufrufer Ihrer Methode ist nicht für die Freigabe des Objekts verantwortlich, da es keine +alloc, +new, -copy oder -mutableCopy Methode ist, aber Sie müssen sicherstellen, dass es schließlich freigegeben wird. In diesem Fall rufen Sie -autorelease manuell auf dem Objekt auf, bevor Sie es zurückgeben. Zum Beispiel:

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

Wenn Sie das Array über +arrayWithCapacity: erstellen, werden Sie nicht eine der „speziellen“ Methoden aufrufen, so dass Sie nicht das Ergebnis freigeben müssen. Dies wird wahrscheinlich mit implementiert, ähnlich wie im letzten Beispiel, aber nicht unbedingt. (Übrigens können Sie auch ein leeres, automatisch freigegebenes NSMutableArray mit [NSMutableArray array] erstellen; die Methode befindet sich in NSArray. Daher wird sie in der Dokumentation unter NSMutableArray nicht angezeigt, aber beim Senden an die NSMutableArray-Klasse wird ein veränderbares Array erstellt.) If Sie werden das Array von Ihrer Methode zurückgeben, Sie können dies als Kurzschrift für [[[NSMutableArray alloc] init] autorelease] -aber es ist nur eine Abkürzung. In vielen Situationen können Sie jedoch ein Objekt mit -init oder +new erstellen und es zum richtigen Zeitpunkt manuell freigeben.

4

Cocoa verwendet bestimmte Namenskonventionen. Alles, was mit alloc, new oder copy beginnt, gibt etwas mit einem retainCount von 1 zurück, und Sie müssen es freigeben. Alles andere, was eine Funktion zurückgibt, hat ein ausgewogenes RetainCount (es könnte von etwas anderem gehalten werden oder es könnte beibehalten und freigegeben werden). So

:

NSMutableArray *removals = [NSMutableArray new]; 

Hat eine retainCount von 1, und:

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

oder

NSMutableArray *removals = [NSMutableArray array]; 

nicht tun, da die Verfahren nicht mit alloc voran, neue oder Kopieren. Dies ist alles in der Speicherverwaltung documentation buchstabiert. Insbesondere gilt Folgendes:

Sie nehmen Besitz eines Objekts, wenn Sie es mit einer Methode, deren Namen erstellen beginnt mit „alloc“ oder „neu“ oder enthält „Kopie“ (zum Beispiel alloc, newObject, oder mutableCopy), oder wenn Sie senden Sie eine Retain-Nachricht. Sie sind verantwortlich für den Verzicht auf Besitz von Objekten, die Sie besitzen Release oder Autorelease. Jedes andere Mal erhalten Sie ein Objekt, das Sie nicht freigeben müssen.

+1

** Alle ** dieser Methoden geben ein Objekt mit einer Retain-Anzahl von 1 zurück. Der Unterschied besteht nur darin, dass Sie das Objekt besitzen und es daher freigeben müssen und mit den anderen nicht Objekt und sind nicht erforderlich, um es freizugeben (aber kann auch nicht darauf zählen, dass es um die aktuelle Anrufkette herum ist). – Chuck

+2

Streng genommen, nein, das ist ein Implementierungsdetail. In einer Reihe von Fällen geben sie Dinge mit unterschiedlichen Rücklagen zurück. Zum Beispiel = [UIImage imageNamed:] kann etwas mit einem sehr großen großen retainCount zurückgeben, weil es ein zwischengespeichertes Bild wiederverwenden könnte. –

+1

Nun ja, die Retain-Zählung selbst ist ein Implementierungsdetail. Apple's Dokumente sagen so viel. Und in allen Fällen, die Sie oben bei aktuellen Versionen von OS X aufgelistet haben, ist der Wert dieses Implementierungsdetails 1. – Chuck

7

Dies ist, wie die Dinge hinter den Kulissen implementiert:

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

und

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

Im ersten Fall wird das Array nur zugewiesen, und Sie sind für de-Zuweisung verantwortlich. Im Gegensatz dazu hat das ArrayWithCapacity automatisch für Sie freigegeben und wird kein Leck verursachen, selbst wenn Sie vergessen, die Zuordnung aufzuheben.