2009-02-09 9 views
12

Ich bin neu im Codieren und versuche, mit Objective-C auf den neuesten Stand zu kommen. Kam über einen Code, den ich nicht verstanden habe. Ich hatte gehofft, jemand könnte es für mich klären. Im folgenden Fall bin ich mir nicht sicher, wie * foo2 funktioniert und warum es nicht veröffentlicht wird?Objective-C-Zeiger?

ClassOne *pointer = [[ClassOne alloc]init]; 

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo2 = [foo add: pointer]; 
[foo release]; 
foo = foo2 

[pointer release]; 

[foo release]; 
+0

Wie lauten die Klassennamen dieser Klassen? –

+2

Übrigens bin ich nicht sicher, ob dies im Prozess der Anonymisierung des Codes oder was geschah, aber dieser Code ist ziemlich verwirrend, wenn man bedenkt, dass er nur sieben Zeilen umfasst. – Chuck

Antwort

1

Weil Sie es nicht veröffentlicht haben. Du veröffentlichst hier Verweise und gibst keine Zeiger frei. Es ist nicht ganz dasselbe. Wenn Sie [foo release] das zweite Mal ausführen, geben Sie die foo-Referenz frei, die Sie beim Zuweisen von foo2 zu foo erstellt haben.

Um die Referenz foo2 freizugeben, müssen Sie die Freigabe für diese Referenz aufrufen, nicht eine Kopie der Referenz.

3
ClassOne *pointer = [[ClassOne alloc]init]; 

Die Variable pointer ist ein Zeiger auf ein Objekt der Klasse ClassOne. Es hat den Wert eines neu erstellten Objekts zugewiesen.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

*foo und *foo2 sind Objekte der Klasse ClassTwo. Nur foo wird ein neu erstelltes Objekt zugewiesen. foo2 kann auf etwas verweisen, daher ist es unsicher, es zu verwenden, bevor Sie ihm einen Wert zuweisen.

foo2 = [foo add: pointer]; 

foo2 wird ein Wert zugewiesen: Ich gehe davon aus, dass die add: Nachricht der Klasse ClassTwo ein Objekt erstellt (die Signatur der Methode sollte -(ClassTwo*)add:(ClassOne*); sein)

[foo release]; 

Der Gegenstand wies auf keine von foo ist länger benötigt. beide auf dasselbe Objekt:

foo = foo2; 

Die Variable foo wird, um den Wert von foo2 zugeordnet.

[pointer release]; 

Das Objekt, auf das pointer zeigt, wird nicht mehr benötigt.

[foo release]; 

Das Objekt, auf das durch foo (und auch von foo2) nicht mehr benötigt wird.

+0

Eigentlich sollte die Signatur der Methode sein - (ClassTwo *) add: (ClassOne *); - In seinem Beispiel ist der Empfänger eine Instanz von ClassTwo, nicht die Klasse selbst. –

+0

Ja, du hast Recht! Ich aktualisiere meine Antwort, Danke. – mouviciel

1

Von Ihrem einfachen Beispiel aus ist es wirklich schwer zu sagen, was vor sich geht. In der Regel geben Methoden vom Typ "class add:" den Wert void zurück, sodass sie eine Compilerwarnung auslösen sollten, dass der Wert "void value nicht ignoriert wird, wie er sein sollte".

Also ohne weitere Informationen ist es ein bisschen schwierig, die Dinge herauszufinden.

Ein paar Dinge im Auge zu behalten:

  • Sie Befehle 'Null' in objc senden. Wenn also [foo add: pointer] nil zurückgibt, können Sie den ganzen Tag "release" ohne Auswirkungen aufrufen.

  • retainCount ist Ihr Freund. Sie können es für jedes NSObject aufrufen, um zu sehen, wie viele Objekte es festhält. Dies kann Ihnen auch helfen, das Problem zu finden.

  • Schließlich ist Garbage Collection aktiviert?

1

Das hängt wirklich davon ab, was [foo add:pointer]; tut. Es sieht so aus, als ob es eine Kopie von foo zurückgibt und es behält. Dies ist eindeutig ein schlechtes Design, da aus der Methode ersichtlich sein sollte, ob das zurückgegebene Objekt eine Kopie/Referenz ist. Methoden mit dem Namen add: sollten keine Kopie zurückgeben.

Schritt für Schritt:

// this somehow creates a retained copy of foo. 
foo2 = [foo add:pointer]; 

// foo is released and gets destroyed. 
[foo release]; 

// makes foo point to the same object as foo2 
// (`foo` has no connection to the former object anymore) 
foo = foo2; 

// foo (and foo2 as they point to the same object) are released 
[foo release]; 
0

Was Sie wirklich versuchen, sich darauf zu konzentrieren, wie Zeiger Arbeit. Ich denke, Sie haben den Unterschied zwischen Zeigern und Objekten noch nicht verstanden.

Here's the link to pointers on wikipedia.

22

Mit Objective-C Cocoa arbeiten wir mit halbautomatischer Referenzzählspeicherverwaltung. Beim Zuweisen von Speicher für ein Objekt, Beibehalten eines Objekts oder Aufrufen einer copy-Methode für ein Objekt wird die Retain-Anzahl (Referenzzählung) um 1 erhöht. Beim Aufrufen von release für ein Objekt wird die Retain-Anzahl um eins verringert. Wenn autorelease für ein Objekt aufgerufen wird, wird release zu einem späteren Zeitpunkt (während der Hauptlaufschleife, wenn keiner von Ihrem eigenen Code ausgeführt wird) für das Objekt aufgerufen, so dass es die Referenz nicht wie Sie unter Ihnen herauszieht versuche es zu benutzen). Wenn die Retain-Anzahl 0 erreicht, kann das Objekt freigegeben werden.

Im Allgemeinen, wenn Sie retain auf ein Objekt anrufen, sind Sie Ihr Interesse an ihm signalisiert, und Sie sind ein release oder autorelease Anruf zu einem bestimmten Zeitpunkt für die Herstellung verantwortlich, wenn Sie nicht mehr in dem Objekt interessiert sind . Wenn Sie für ein Objekt die Methode alloc oder copy aufrufen, haben Sie Ihr Interesse an dem Objekt signalisiert und müssen es mit einem release oder autorelease irgendwo auf der anderen Seite übereinstimmen.

Dieser Link deckt so ziemlich die Richtlinien von Apple verwendet (und Sie sollten verwenden) für die Speicherverwaltung: Simple rules for memory management in Cocoa

der durch die Linie durch den Code Zeile Lassen Sie gehen:

ClassOne *pointer = [[ClassOne alloc]init]; 

pointer Punkte auf ein neu zugewiesenes ClassOne-Objekt mit einem Retain-Zählerstand von 1, da wir Alloc darauf aufgerufen haben. Wir sind verpflichtet, die Nummer release oder autorelease auf pointer irgendwann in der Zukunft zu nennen.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo Punkte auf ein neu Objekt ClassTwo zugeordnet ist, mit einer Zahl von 1 beibehalten, da wir alloc sie aufgefordert haben. Wir sind verpflichtet, die Nummer release oder autorelease auf foo irgendwann in der Zukunft zu nennen.

foo2 zeigt gerade jetzt nichts besonderes an. Es ist nicht sicher zu benutzen.

foo2 = [foo add: pointer]; 

pointer hat foo hinzugefügt (was immer das bedeutet, wir die Umsetzung nicht kennen). foo könnte retain auf pointer aufgerufen haben, um sein Interesse daran zu signalisieren, und es als Feld hinzugefügt, oder es pointer zu einer Sammlung hinzugefügt haben (in diesem Fall ist es die Verantwortung der Sammlung retain darauf, wenn ein Objekt hinzugefügt wird, und release wenn ein Objekt entfernt wird). In jedem Fall hat dies keine Auswirkungen auf unseren Codeblock. Daher ist es uns egal, was unter der Haube passiert.

Die von dieser Methode zurückgegebene Referenz könnte pointer selbst sein, oder es könnte eine automatisch freigegebene Kopie von pointer sein; Wir haben keinen Zugriff auf die API oder die Implementierung, um uns mitzuteilen, welche.

In beiden Fällen ist es nicht unsere Verantwortung, release für dieses Objekt aufzurufen. Wenn die Methode copy im Namen oder retain für die zurückgegebene Referenz (wie foo2 = [[foo add:pointer] retain];) aufgerufen hätte, wäre die Retain-Zählung um 1 inkrementiert worden, und es wäre in unserer Verantwortung, release oder autorelease darauf anzurufen.

[foo release]; 

Die von foo referenzierte Objekt freigegeben wurde, was bedeutet, seine Beibehaltungszähler um 1. Für dieses Beispiel verringert wurde, ist dieser Paare mit dem alloc Anruf wir in Zeile 2 hergestellt, so dass die Zählung behalten wird auf 0 gesetzt, wodurch foo für die Freigabe freigegeben wird.

Im Allgemeinen ist es uns egal, ob das Objekt freigegeben wurde oder nicht; Wir müssen nur sicherstellen, dass wir alle alloc, copy oder retain Anrufe mit der gleichen Nummer von release oder autorelease Anrufe paaren. Wenn wir zu irgendeinem Zeitpunkt ein Interesse an einem Objekt anmelden, liegt es in unserer Verantwortung, unser Interesse freizugeben, andernfalls werden wir Speicherlecks haben.

foo = foo2; 

foo verweist nun auf das gleiche durch foo2 referenzierte Objekt. Denken Sie daran, wir haben keine alloc oder copy Methode aufgerufen, als wir foo2 erhielten, noch haben wir ein Interesse daran registriert, indem wir retain aufrufen. Da wir keine Verantwortung haben, release unter foo2 anzurufen, sind wir nicht verpflichtet, release unter foo anzurufen.

[pointer release]; 

pointer ‚s behalten Zahl um 1 verringert wurde es im Mai dieses Jahres haben brachte seine Beibehaltungszähler auf 0 oder nicht, es hängt davon ab, welche foo tat, als wir es hinzugefügt. Trotzdem ist uns das egal. Wir haben unsere Verantwortung zu pointer beendet, indem wir release darauf anrufen, um mit dem alloc Anruf übereinzustimmen, den wir am Anfang bildeten. Obwohl pointer nach diesem Aufruf möglicherweise noch vorhanden ist, können wir diese Annahme nicht machen, und alles mit dem zuvor durch Zeiger referenzierten Objekt zu tun, wäre ein Fehler (obwohl wir pointer ändern könnten, um auf etwas anderes frei zu zeigen).

[foo release]; 

Wenn der Autor dieses Codes hat die Speicherverwaltung Konventionen des verfolge Apfels, dann ist dies nicht notwendig. Wir haben keine Verantwortung, release auf foo oder foo2 aufzurufen (sie verweisen auf das gleiche Objekt, nicht vergessen). Dies führt nicht zum Bruch des Codes. etwas auf eine nil Referenz zu nennen ist im Wesentlichen ein No-Op. Es kann jedoch für jeden, der den Code überprüft, Verwirrung stiften.

Nun, der Autor dieses Codes möglicherweise die Speicherverwaltung Konventionen gebrochen. Er könnte dafür gesorgt haben, dass add Anruf eine Kopie von pointer ohne Anruf autorelease darauf zurückgibt, in diesem Fall macht es den Anrufer verantwortlich für den Anruf release darauf. Dies ist eine sehr schlechte Form, und wenn Sie Code ausführen sollten, der die Speicherverwaltungskonvention durchbricht, dokumentieren Sie, wo Sie ihn verwenden und wie er die Konvention bricht, um in Zukunft Verwirrung zu vermeiden.

+3

Die [foo release] am Ende wird wahrscheinlich * einen Absturz verursachen, wenn wir die Richtlinien zur Speicherverwaltung befolgen. Es wird auf das Ergebnis von [foo add: pointer] gesetzt, von dem wir nicht wissen, dass es null ist. Wenn es etwas anderes als Null ist, Programm gehen Boom. – Chuck

1

Wow, vielen Dank für die tollen Antworten!

Ich denke, was ich wirklich gemeint bin, ist ein Verweis auf ein Objekt und kein Zeiger. Nichtsdestotrotz denke ich, dass die angehängte , *foo2 im selben Speicher wie foo liegt. Auch foo2 wird zur selben Zeit wie foo aus dem Speicher freigegeben. Ich habe noch viel mehr zu tun, aber einen Tag nach dem anderen!