Objective-C Kurz gesagt Speicherverwaltung

Für jedes von Ihrer Anwendung verwendete Objekt muss Speicher zugewiesen werden, und es muss freigegeben werden, wenn Ihre Anwendung damit fertig ist, um sicherzustellen, dass Ihre Anwendung den Speicher so effizient wie möglich verwendet. Es ist wichtig, die Speicherverwaltungsumgebung von Objective-C zu verstehen, um sicherzustellen, dass Ihr Programm keinen Speicher verliert oder auf Objekte verweist, die nicht mehr vorhanden sind.

Verweise auf ein Objekt zählen

Im Gegensatz zu C # ist Objective-C der Fall nicht Müllsammlung verwenden. Stattdessen wird eine Referenzzählerumgebung verwendet, in der nachverfolgt wird, an wie vielen Stellen ein Objekt verwendet wird. Solange mindestens ein Verweis auf das Objekt vorhanden ist, stellt die Objective-C-Laufzeitumgebung sicher, dass sich das Objekt im Speicher befindet. Wenn es jedoch keine Verweise mehr auf das Objekt gibt, darf die Laufzeitumgebung das Objekt freigeben und den Speicher für etwas anderes verwenden. Wenn Sie versuchen, auf ein Objekt zuzugreifen, nachdem es freigegeben wurde, stürzt das Programm höchstwahrscheinlich ab.

Es gibt zwei sich gegenseitig ausschließende Methoden zum Verwalten von Objektreferenzen in Objective-C:

  1. Senden Sie manuell Methoden, um die Anzahl der Referenzen auf ein Objekt zu erhöhen oder zu verringern.
  2. Lassen Sie das neue automatische Referenzzählungsschema (ARC) von Xcode 4.2 (und später) für Sie erledigen.

ARC ist die bevorzugte Methode zum Verwalten des Speichers in neuen Anwendungen. Dennoch ist es wichtig zu wissen, was unter der Haube abläuft. Im ersten Teil dieses Kapitels erfahren Sie, wie Sie manuell Objektreferenzen nachverfolgen. Anschließend werden die praktischen Auswirkungen von ARC erläutert.


Manuelle Speicherverwaltung

Um mit dem Code in diesem Abschnitt zu experimentieren, müssen Sie die automatische Referenzzählung deaktivieren. Sie können dies tun, indem Sie auf klicken HalloObjektivC Projekt im Navigationsbereich von Xcode:

Das HelloObjectiveC-Projekt in der Navigationsleiste

Es öffnet sich ein Fenster, in dem Sie die Build-Einstellungen für das Projekt anpassen können. In der zweiten Hälfte dieser Serie werden wir die Build-Einstellungen besprechen. Jetzt müssen wir nur noch die ARC-Flagge finden. Geben Sie im Suchfeld in der rechten oberen Ecke ein automatische Referenzzählung, und Sie sollten die folgende Einstellung sehen:

Deaktivieren der automatischen Referenzzählung

Klicken Sie auf die Pfeile neben Ja und ändern Sie es in Nein um ARC für dieses Projekt zu deaktivieren. Auf diese Weise können Sie die in den folgenden Abschnitten beschriebenen Speicherverwaltungsmethoden verwenden.

Bei der manuellen Speicherverwaltung (auch manuelles Retain-Release oder MMR genannt) geht es um das Konzept des Objekts "Inhaberschaft". Wenn Sie ein Objekt erstellen, werden Sie dazu aufgefordert besitzen das Objekt - es liegt in Ihrer Verantwortung, das Objekt zu befreien, wenn Sie damit fertig sind. Dies ist sinnvoll, da Sie nicht möchten, dass ein anderes Objekt mitkommt und das Objekt freigibt, während Sie es verwenden.

Objektbesitz wird durch Referenzzählung implementiert. Wenn Sie den Besitz eines Objekts beanspruchen, erhöhen Sie dessen Referenzanzahl um eins, und wenn Sie den Besitz aufgeben, dekrementieren Sie den Referenzzähler um eins. Auf diese Weise kann sichergestellt werden, dass ein Objekt niemals aus dem Speicher freigegeben wird, während ein anderes Objekt es verwendet. NSObject und das NSObject-Protokoll definieren die vier Kernmethoden, die den Besitz von Objekten unterstützen:

  • +(id) zuteilen - Weisen Sie Speicher für eine neue Instanz zu und beanspruchen Sie den Besitz dieser Instanz. Dies erhöht die Referenzanzahl des Objekts um eins. Es gibt einen Zeiger auf das zugewiesene Objekt zurück.
  • -(id) behalten - Inanspruchnahme des Eigentums an einem vorhandenen Objekt. Es ist möglich, dass ein Objekt mehrere Besitzer hat. Dies erhöht auch die Referenzzählung des Objekts. Es gibt einen Zeiger auf das vorhandene Objekt zurück.
  • -(nichtig) freigeben - Überlassen Sie den Besitz eines Objekts. Dies verringert den Referenzzähler des Objekts.
  • -(id) autorelease - Überlassen Sie den Besitz eines Objekts am Ende des aktuellen Blockes für den Autorelease-Pool. Dadurch wird der Referenzzähler des Objekts verringert, aber Sie können das Objekt weiterhin verwenden, indem Sie die tatsächliche Freigabe bis zu einem späteren Zeitpunkt verschieben. Es gibt einen Zeiger auf das vorhandene Objekt zurück.

Für jeden zuteilen oder behalten Methode, die Sie anrufen, müssen Sie anrufen Veröffentlichung oder Autorelease irgendwann auf der ganzen Linie. Die Anzahl, wie oft Sie ein Objekt beanspruchen Muss entspricht der Anzahl, wie oft Sie es freigeben. Extra anrufen zuteilen/behalten führt zu einem Speicherverlust und dem Aufruf eines Extra Veröffentlichung/Autorelease versucht, auf ein Objekt zuzugreifen, das nicht vorhanden ist, wodurch Ihr Programm abstürzt.

Alle Ihre Objektinteraktionen - unabhängig davon, ob Sie sie in einer Instanzmethode, einem Getter / Setter oder einer eigenständigen Funktion verwenden - sollten dem Claim / Use / Free-Muster folgen, wie im folgenden Beispiel gezeigt:

Enthaltenes Codebeispiel: Manueller Speicher

int main (int argc, const char * argv []) // Objekt beanspruchen. Person * frank = [[Personenzuweisung] init]; // Verwenden Sie das Objekt. frank.name = @ "Frank"; NSLog (@ "% @", frank.name); // Das Objekt freigeben. [Frank Release]; 0 zurückgeben; 

Das [Personenzuteilung] Anrufsätze frankReferenzzähler auf Eins, und [Frank Release] dekrementiert sie auf null, sodass die Laufzeit über sie verfügen kann. Beachten Sie, dass versucht wird, einen anderen anzurufen [Frank Release] würde zu einem Absturz führen, da die frank Variable existiert nicht mehr im Speicher.

Bei der Verwendung von Objekten als lokale Variable in einer Funktion (z. B. im vorherigen Beispiel) ist die Speicherverwaltung ziemlich einfach: einfach aufrufen Veröffentlichung am Ende der Funktion. Es kann jedoch schwieriger werden, Eigenschaften innerhalb von Setter-Methoden zuzuweisen. Betrachten Sie beispielsweise die folgende Schnittstelle für eine neue Klasse mit dem Namen Schiff:

Enthaltenes Codebeispiel: Manual Memory - schwache Referenz

// Ship.h #import "Person.h" @interface Schiff: NSObject - (Person *) captain; - (ungültig) setCaptain: (Person *) theCaptain; @Ende

Dies ist eine sehr einfache Klasse mit manuell definierten Zugriffsmethoden für a Kapitän Eigentum. Aus Sicht der Speicherverwaltung gibt es mehrere Möglichkeiten, den Setter zu implementieren. Nehmen Sie zunächst den einfachsten Fall, bei dem der neue Wert einfach einer Instanzvariablen zugewiesen wird:

// Ship.m #import "Ship.h" @implementation Ship Person * _captain;  - (Person *) Kapitän Rückkehr _Kapitän;  - (void) setCaptain: (Person *) theCaptain _captain = theCaptain;  @Ende

Dies schafft eine schwache Referenz weil der Schiff Instanz übernimmt nicht den Besitz der der Kapitän Objekt, wenn es zugewiesen wird. Obwohl daran nichts auszusetzen ist und Ihr Code weiterhin funktioniert, ist es wichtig, die Auswirkungen schwacher Verweise zu verstehen. Betrachten Sie den folgenden Ausschnitt:

#einführen  #import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool Person * frank = [[Personallokation] init]; Schiff * discoveryOne = [[Schiffszuteilung] init]; frank.name = @ "Frank"; [discoveryOne setCaptain: frank]; NSLog (@ "% @", [discoveryOne captain] .name); [Frank Release]; // [discoveryOne captain] ist jetzt ungültig. NSLog (@ "% @", [discoveryOne captain]. Name); [discoveryOne release];  return 0; 

Berufung [Frank Release] Dekremente frankReferenzzähler auf Null, was bedeutet, dass die Laufzeit es freigeben darf. Das bedeutet, dass [Entdeckung eines Kapitäns] zeigt jetzt auf eine ungültige Speicheradresse, obwohl discoveryOne hat es nie veröffentlicht.

In dem bereitgestellten Beispielcode werden Sie feststellen, dass wir a hinzugefügt haben Dealloc Überschreiben der Methode in der Person-Klasse. Dealloc wird aufgerufen, wenn der Speicher freigegeben wird. Wir sollten normalerweise damit umgehen Dealloc und geben Sie alle verschachtelten Objektverweise frei, die wir halten. In diesem Fall geben wir die verschachtelte Namenseigenschaft frei, die wir besitzen. Wir werden mehr zu sagen haben Dealloc im nächsten Kapitel.

Wenn Sie versuchen, auf die Eigenschaft zuzugreifen, würde Ihr Programm höchstwahrscheinlich abstürzen. Wie Sie sehen, müssen Sie Objektverweise sehr genau verfolgen, wenn Sie schwach referenzierte Eigenschaften verwenden.

Schwacher Bezug zum Kapitänwert

Für robustere Objektbeziehungen können Sie starke Referenzen verwenden. Diese werden erstellt, indem Sie das Objekt mit a beanspruchen behalten anrufen, wenn es zugewiesen ist:

Enthaltenes Codebeispiel: Manueller Speicher - starke Referenz

- (void) setCaptain: (Person *) theCaptain [_captain autorelease]; _captain = [theCaptain behalten]; 

Bei einer starken Referenz spielt es keine Rolle, was andere Objekte mit dem Objekt tun der Kapitän Objekt, da behalten stellt sicher, dass es so lange bleibt wie Schiff Instanz braucht es. Natürlich müssen Sie das ausgleichen behalten Aufruf durch Freigeben des alten Werts. Wenn dies nicht der Fall ist, würde Ihr Programm Speicherplatz verlieren, wenn jemand dem neuen einen neuen Wert zugewiesen hat Kapitän Eigentum.

Starker Hinweis auf den Kapitänwert

Objekte automatisch freigeben

Das Autorelease Methode funktioniert sehr ähnlich Veröffentlichung, Der Referenzzähler des Objekts wird jedoch nicht sofort dekrementiert. Stattdessen wartet die Laufzeit bis zum Ende des aktuellen @ autoreleasepool blockieren, um eine normale aufzurufen Veröffentlichung auf dem Objekt. Deshalb ist das main.m Die Vorlage ist immer in eine @ autoreleasepool-Es stellt sicher, dass alle Objekte in der Warteschlange sind Autorelease Anrufe sind tatsächlich am Ende des Programms veröffentlicht:

int main (int argc, const char * argv []) @autoreleasepool // Fügen Sie hier Code ein, um Objekte zu erstellen und freizugeben. NSLog (@ "Hallo, Welt!"); // Alle automatisch freigegebenen Objekte werden hier * tatsächlich * veröffentlicht.  return 0; 

Die Idee hinter dem automatischen Freigeben ist, dem Besitzer eines Objekts die Möglichkeit zu geben, den Besitz aufzugeben, ohne das Objekt tatsächlich zu zerstören. Dies ist ein notwendiges Werkzeug in Situationen, in denen Sie ein neues Objekt aus einer Factory-Methode zurückgeben müssen. Betrachten Sie beispielsweise die folgende Klassenmethode, die in definiert ist Ship.m:

+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Schiffszuordnung] init]; [theShip setCaptain: theCaptian]; kehre das Schiff zurück; 

Diese Methode erstellt, konfiguriert und gibt ein neues aus Schiff Beispiel. Bei dieser Implementierung gibt es jedoch ein ernstes Problem: Sie führt zu einem Speicherverlust. Die Methode gibt niemals den Besitz des Objekts und des Aufrufers von auf shipWithCaptain Ich weiß nicht, dass sie das zurückgegebene Objekt freigeben müssen (auch nicht). Als Ergebnis der das Schiff Objekt wird niemals aus dem Speicher freigegeben. Genau das ist die Situation Autorelease wurde für entworfen. Die korrekte Implementierung wird hier gezeigt:

+ (Ship *) shipWithCaptain: (Person *) theCaptian Ship * theShip = [[Schiffszuordnung] init]; [theShip setCaptain: theCaptian]; return [theShip Autorelease]; // Muss das Eigentum abgeben! 

Verwenden Autorelease anstatt eines sofortigen Veröffentlichung ermöglicht dem Aufrufer, das zurückgegebene Objekt zu verwenden, während der Besitz des Objekts an der richtigen Stelle aufgegeben wird. Wenn Sie sich aus dem Kapitel Datentypen erinnern, haben wir alle unsere Foundation-Datenstrukturen mithilfe von Factory-Methoden auf Klassenebene erstellt. Zum Beispiel:

NSSet * crew = [NSSet setWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", nil];

Das setWithObjects Methode funktioniert genau wie die shipWithCaptain Methode, die im vorherigen Beispiel beschrieben wurde. Es gibt ein automatisch freigegebenes Objekt zurück, sodass der Aufrufer das Objekt verwenden kann, ohne sich um die Speicherverwaltung kümmern zu müssen. Beachten Sie, dass es gleichwertige Instanzmethoden zum Initialisieren von Foundation-Objekten gibt. Zum Beispiel die Crew Das Objekt in der letzten Probe kann wie folgt manuell erstellt werden:

// Set erstellen und beanspruchen. NSSet * crew = [[NSSet zuteilen] initWithObjects: @ "Dave", @ "Heywood", @ "Frank", @ "HAL", nil]; // Set verwenden… // Set loslassen. [Crew Release];

Verwenden Sie jedoch Klassenmethoden wie setWithObjects, arrayWithCapacity, etc. ist im Allgemeinen gegenüber dem bevorzugt zuteilen/drin.

Manuelle Retain-Release-Attribute

Der Umgang mit dem Speicher hinter den Eigenschaften eines Objekts kann eine langwierige, sich wiederholende Aufgabe sein. Zur Vereinfachung des Prozesses enthält Objective-C mehrere Eigenschaftsattribute zur Automatisierung der Speicherverwaltungsaufrufe in Accessor-Funktionen. Die in der folgenden Liste beschriebenen Attribute definieren das Setterverhalten in Handbuch Referenzzählumgebungen. Tun nicht versuchen zu benutzen zuordnen und behalten in einer automatischen Referenzzählumgebung.

  • zuordnen - Speichern Sie einen direkten Zeiger auf den neuen Wert ohne behalten / Veröffentlichung Anrufe. Dies ist das automatisierte Äquivalent einer schwachen Referenz.
  • behalten - Speichern Sie einen direkten Zeiger auf den neuen Wert, rufen Sie jedoch auf Veröffentlichung auf den alten Wert und behalten auf dem neuen. Dies ist das automatisierte Äquivalent einer starken Referenz.
  • Kopieren - Erstellen Sie eine Kopie des neuen Werts. Der Besitz der neuen Instanz wird kopiert, so dass der vorherige Wert gesendet wird Veröffentlichung Botschaft. Dies ist wie ein starker Hinweis auf eine brandneue Instanz des Objekts. Im Allgemeinen wird das Kopieren nur für unveränderliche Typen wie verwendet NSString.

Untersuchen Sie als einfaches Beispiel die folgende Eigenschaftsdeklaration:

@ Eigenschaft (behalten) Person * Kapitän;

Das behalten Attribut teilt dem verknüpften @ synthetisieren Deklaration, um einen Setter zu erstellen, der ungefähr so ​​aussieht:

- (void) setCaptain: (Person *) theCaptain [_captain release]; _captain = [theCaptain behalten]; 

Wie Sie sich vorstellen können, verwenden Sie Speicherverwaltungsattribute mit @Eigentum ist viel einfacher als das manuelle Definieren von Gettern und Setters für jede Eigenschaft jeder benutzerdefinierten Klasse, die Sie definieren.


Automatisches Referenzzählen

Da Sie nun die Referenzzählung, den Besitz von Objekten und die Blöcke für die Autorelease in den Griff bekommen haben, können Sie alles komplett vergessen. Ab Xcode 4.2 und iOS 4 unterstützt Objective-C die automatische Referenzzählung (ARC), einen Vor-Kompilierungsschritt, der die erforderlichen Speicherverwaltungsaufrufe für Sie hinzufügt.

Wenn Sie ARC im vorherigen Abschnitt deaktiviert haben, sollten Sie ihn wieder einschalten. Denken Sie daran, dass Sie dies tun können, indem Sie auf das Symbol klicken HalloObjektivC Projekt in der Navigationsleiste, wählen Sie die Einstellungen erstellen Registerkarte und Suche nach automatische Referenzzählung.

Aktivieren der automatischen Referenzzählung in den Build-Einstellungen des Projekts

Die automatische Referenzzählung funktioniert, indem Sie Ihren Code untersuchen, um herauszufinden, wie lange ein Objekt verbleiben und einfügen muss behalten, Veröffentlichung, und Autorelease Methoden, um sicherzustellen, dass es freigegeben wird, wenn es nicht mehr benötigt wird, aber nicht, wenn Sie es verwenden. Um den ARC-Algorithmus nicht zu verwirren, Sie darf nicht machen Sie irgendwelche behalten, Veröffentlichung, oder Autorelease ruft dich an Mit ARC können Sie beispielsweise die folgende Methode und keine schreiben das Schiff Noch der Kapitän wird durchgesickert, auch wenn wir den Besitz nicht explizit aufgegeben haben:

Enthaltenes Codebeispiel: ARC

+ (Schiff *) Schiff Schiff * theShip = [[Schiffszuteilung] init]; Person * theCaptain = [[Personenzuweisung] init]; [theShip setCaptain: theCaptain]; kehre das Schiff zurück; 

ARC-Attribute

In einer ARC-Umgebung sollten Sie das nicht mehr verwenden zuordnen und behalten Eigenschaftsattribute. Verwenden Sie stattdessen die schwach und stark Attribute:

  • schwach - Geben Sie eine nicht besitzende Beziehung zum Zielobjekt an. Das ist sehr ähnlich zuordnen; Es hat jedoch die praktische Funktionalität, die Eigenschaft auf festzulegen Null wenn der Wert freigegeben wird. Auf diese Weise stürzt Ihr Programm nicht ab, wenn versucht wird, auf eine ungültige Speicheradresse zuzugreifen.
  • stark - Geben Sie eine Eigentümerbeziehung zum Zielobjekt an. Dies ist das ARC-Äquivalent von behalten. Es stellt sicher, dass ein Objekt nicht freigegeben wird, solange es der Eigenschaft zugewiesen ist.

Sie können den Unterschied zwischen schwach und stark anhand der Implementierung von erkennen Schiff Klassenmethode aus dem vorherigen Abschnitt. Um einen starken Bezug zum Schiffskapitän herzustellen, wird die Schnittstelle für Schiff sollte wie folgt aussehen:

// Ship.h #import "Person.h" @interface Schiff: NSObject @property (strong) Person * Kapitän; + (Schiff *) Schiff; @Ende

Und die Umsetzung Schiff Sollte aussehen, wie:

// Ship.m #import "Ship.h" @implementation Ship @synthesize captain = _captain; + (Schiff *) Schiff Schiff * theShip = [[Schiffszuteilung] init]; Person * theCaptain = [[Personenzuweisung] init]; [theShip setCaptain: theCaptain]; kehre das Schiff zurück;  @Ende

Dann können Sie sich ändern main.m Um den Schiffskapitän anzuzeigen:

int main (int argc, const char * argv []) @autoreleasepool Schiff * Schiff = [Schiff Schiff]; NSLog (@ "% @", [Schiffskapitän]);  return 0; 

Dies wird etwas wie ausgeben in der Konsole, die uns sagt, dass die der Kapitän Objekt erstellt in der Schiff Klassenmethode existiert noch.

Aber versuche das zu ändern (stark) Eigenschaftsattribut an (schwach) und das Programm erneut kompilieren. Jetzt solltest du sehen (Null) im Ausgabefeld. Die schwache Referenz stellt nicht sicher, dass die der Kapitän Variable bleibt herum, also sobald sie am Ende der Schiff Klassenmethode glaubt der ARC-Algorithmus, dass er über sie verfügen kann der Kapitän. Als Ergebnis der Kapitän Eigenschaft ist auf gesetzt Null.


Zusammenfassung

Die Speicherverwaltung kann sehr schmerzhaft sein, ist jedoch ein wesentlicher Bestandteil beim Erstellen einer Anwendung. Für iOS-Anwendungen ist die ordnungsgemäße Objektzuordnung / -beseitigung aufgrund der begrenzten Speicherressourcen mobiler Geräte besonders wichtig. Wir werden im zweiten Teil dieser Serie mehr darüber sprechen, iOS Prägnant.

Glücklicherweise vereinfacht das neue ARC-Schema die durchschnittliche Speicherverwaltung für den durchschnittlichen Entwickler. In den meisten Fällen ist es möglich, ein ARC-Projekt wie eine Garbage Collection in einem C # -Programm zu behandeln. Erstellen Sie einfach Ihre Objekte und lassen Sie sie von ARC nach eigenem Ermessen entsorgen. Beachten Sie jedoch, dass dies nur eine praktische Ähnlichkeit ist - die ARC-Implementierung ist viel effizienter als die Speicherbereinigung.

Diese Lektion stellt ein Kapitel von Objective-C Succinctly dar, ein kostenloses eBook des Teams von Syncfusion.