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ählenIm 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:
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.
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 NavigationsleisteEs ö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ählungKlicken 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 frank
Referenzzä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 frank
Referenzzä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änwertFü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.
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
.
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.
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 ProjektsDie 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;
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
.
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.