Speicherverwaltung Begriffsdefinition

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Speicherverwaltung Begriffsdefinition

    Folgender Code sei gegeben :

    Quellcode

    1. .....
    2. Person* person1 = [Person new];
    3. Person* person2 = [Person new];
    4. person1 = person2.retain;
    5. .....


    Durch "new" bekommen person1 und person2 einen retainCount von 1;
    Durch person1 = person2.retain bekommen beide einen retainCount von 2:
    person2 ist ersichtlich, das retain steht ja dabei.

    Wie sieht das für person1 aus ? Wie wäre die begrifflich exakte "Erklärung", warum person1 auch um 1 rc zulegt.
    Die Tatsache an sich ist mir zwar verständlich, ich könnte das aber niemandem wasserdicht erklären, ohne begrifflich ziemlich ins Schwimmen zu kommen.

    Hans
  • Hallo,

    "person1" und "person2" sind Zeiger, die auf Objekte gerichtet sind.
    Wenn Du das Objekt, auf das "person2" zeigt, mit seinem Retain-Count erhöhst und dann den Zeiger "person1" auf dieses Objekt richtest, dann liefert es Dir konsequenterweise das gleiche Ergebnis des Retain-Counts.

    Durch "new" bekommen person1 und person2 einen retainCount von 1;

    Ich weiß was Du meinst, aber vielleicht nicht optimal ausgedrückt.
    Durch "new" erzeugst Du ein neues Objekt, auf das ein Zeiger z.B. "person1" gerichtet wird und somit einmal in Verwendung ist.

    Viele Grüße
  • HansGerber schrieb:

    Folgender Code sei gegeben :

    Quellcode

    1. .....
    2. Person* person1 = [Person new];
    3. Person* person2 = [Person new];
    4. person1 = person2.retain;
    5. .....


    Durch "new" bekommen person1 und person2 einen retainCount von 1;
    Durch person1 = person2.retain bekommen beide einen retainCount von 2:
    person2 ist ersichtlich, das retain steht ja dabei.

    Wie sieht das für person1 aus ? Wie wäre die begrifflich exakte "Erklärung", warum person1 auch um 1 rc zulegt.

    Der Begriff, der diesen Codeschnipsel beschreibt ist „Speicherleck“. ;)

    Michael
  • HansGerber schrieb:

    Folgender Code sei gegeben :

    Quellcode

    1. .....
    2. Person* person1 = [Person new];
    3. Person* person2 = [Person new];
    4. person1 = person2.retain;
    5. .....



    Ich hoffe doch sehr, dass die Zeile

    person1 = person2.retain;

    von dir stammt und in keinem Buch der Welt steht.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Der Begriff, der diesen Codeschnipsel beschreibt ist „Speicherleck“. ;)

    Michael
    Das stimmt nicht, schliesslich ist ja nicht der ganze Code abgebildet -> es ist ja nur ein Code-Schnipsel

    ... -> heisst, da kommen noch ein paar Zeilen. Da die aber nicht relevant für die Frage waren, muss ich sie ja nicht abtippen.

    Ich hoffe doch sehr, dass die Zeile

    person1 = person2.retain;

    von dir stammt und in keinem Buch der Welt steht.
    Ja und nein,

    das Beispiel ist etwas abgewandelt :

    Quellcode

    1. MyClass* obj = [[MyClass alloc] init];
    2. obj.field1 = [MyClass alloc] init];
    3. obj.field2 = [MyClass alloc] init];
    4. obj.field2.field1 = [obj.field1 retain];
    5. obj.field2.field2 = [obj.field1 retain];
    6. .....


    Die propertys sind mit assign definiert.

    Ist jetzt nur mein Beispiel Quark oder die Vorlage auch ?
    Hans
  • Vergleiche [object retain]; und object.retain;
    Was fällt dir da auf?

    Und ich sags immer wieder: lernt nicht mit der Punktnotation, die verwirrt nur.
    Nutzt sie erst, wenn ihr wisst, was ihr tut.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • HansGerber schrieb:

    Der Begriff, der diesen Codeschnipsel beschreibt ist „Speicherleck“. ;)

    Michael
    Das stimmt nicht, schliesslich ist ja nicht der ganze Code abgebildet -> es ist ja nur ein Code-Schnipsel

    ... -> heisst, da kommen noch ein paar Zeilen.

    Ob danach noch was kommt ist irrelevant. Da müsste schon zwischen der zweiten und dritten Zeile noch was stehen, um ein Speicherleck zu vermeiden.

    Michael
  • HansGerber schrieb:


    das Beispiel ist etwas abgewandelt :

    Quellcode

    1. MyClass* obj = [[MyClass alloc] init];
    2. obj.field1 = [MyClass alloc] init];
    3. obj.field2 = [MyClass alloc] init];
    4. obj.field2.field1 = [obj.field1 retain];
    5. obj.field2.field2 = [obj.field1 retain];
    6. .....


    Die propertys sind mit assign definiert.

    Ist jetzt nur mein Beispiel Quark oder die Vorlage auch ?
    Hans

    Bereits die Vorlage ist Quark, weil das halten der Referenz Aufgabe des Halters ist und nicht des Zuweisers. Das ist sogar ziemlich fundamental falsch, weil es am Konzept völlig vorbei geht. Ich hoffe doch sehr, dass das erläutert wird.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Also auf gut Deutsch :

    Quellcode

    1. MyClass* obj = [[MyClass alloc] init];
    2. obj.field1 = [MyClass alloc] init];
    3. obj.field2 = [MyClass alloc] init];
    4. [[obj field2] setField1:[obj.field1]];
    5. .....

    wobei das property mit retain gesetzt wird und das Erhöhen des rc dann im Setter erfolgt ?

    Wenn ich die ganze Diskussion um Speichermanagement richtig verstanden habe, dann wäre, wenn ich folgende Zeile anfügte

    Quellcode

    1. obj.field1 = obj.field2;


    in obigem Beispiel auch wieder eine Speicherlücke, weil der Verweis auf das ursprüngliche Object "obj.field1" aus alloc-init verloren geht.
    Müsste ich so was aus irgendwelchen Gründen machen, dann müsste ich entweder auf das vorherige alloc-init verzichten oder eine release setzen.

    Habe ich das richtig verstanden ?

    Hans
  • HansGerber schrieb:


    Quellcode

    1. MyClass* obj = [[MyClass alloc] init];
    2. obj.field1 = [MyClass alloc] init];
    3. obj.field2 = [MyClass alloc] init];
    4. [[obj field2] setField1:[obj.field1]];

    wobei das property mit retain gesetzt wird und das Erhöhen des rc dann im Setter erfolgt ?


    Auch in diesem Beispiel hast Du ein Speicherleck, denn die rechte Seite der Zuweisung ( [ [ MyClass alloc ] init ] bewirkt einen retain-Count von 1, dieser wird dann im Setter (obj.field1) durch das retain nochmals erhöht.
    Im dealloc wird dieser dann (hoffentlich) heruntergezählt, allerdings besteht immer noch der Zähler von obiger Objekterzeugung, welcher nicht ausgeglichen wird.
    Vermeiden könntest Du dies so (bei retain oder copy-Semantik):

    Quellcode

    1. MyClass* obj = ...
    2. obj.field1 = [[[MyClass alloc] init]autorelease];
    3. obj.field2 = [[[MyClass alloc] init]autorelease];

    Oder noch besser mit einem sog. convenience allocator (guckst Du hier).

    Quellcode

    1. @implementation MyClass
    2. + (id)myClass
    3. {
    4. return [ [ [ self alloc ] init ] autorelease ];
    5. }
    6. ...
    7. @end
    8. MyClass* obj = ...
    9. obj.field1 = [MyClass myClass ];
    10. obj.field2 = [MyClass myClass ];

    Für jeden Deiner Initialisierer solltest Du einen entspr. CA haben.
    Die Speicherverwaltung folgt wenigen Prinzipien, wenn Du die einmal verstanden hast und es Dir ins Rückenmark übergegangen ist, geht das alles ganz einfach.

    Gruß, Markus

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Markus Müller ()

  • Auch in diesem Beispiel hast Du ein Speicherleck, denn die rechte Seite der Zuweisung ( [ [ MyClass alloc ] init ] bewirkt einen retain-Count von 1, dieser wird dann im Setter (obj.field1) durch das retain nochmals erhöht.
    Im dealloc wird dieser dann (hoffentlich) heruntergezählt, allerdings besteht immer noch der Zähler von obiger Objekterzeugung, welcher nicht ausgeglichen wird.



    Also müsste ich im Code einmal release setzen (-> wg alloc init) und im dealloc field1 und field2 nil setzen, damit diese über den setter das 2. release bekommen ??


    Hans
  • Amin Negm-Awad schrieb:

    Nein, es wäre richtig, weil der Setter auch die alte Instanz wieder freigibt.

    So wie ich das sehe ist es falsch und HansGerber hat mit seiner Vermutung recht.

    Quellcode

    1. MyObject* object = [[MyObject alloc] init] //x hat nen RC1 durchs alloc
    2. [object setField1:[[MyObject alloc] init]] //y hat nen RC1 durchs alloc, RC2 durch den Retain-Setter
    3. [object setField2:[[MyObject alloc] init]] //z hat nen RC1 durchs alloc, RC2 durch den Retain-Setter
    4. [object setField1:[object field2]] //x RC1, y RC1 durchs Release im Setter, z RC3 durch den zweiten Setter
    5. [object release]; // x RC0 durchs Release, y bleibt bei RC1, z RC1 durchs Release von field1 und field2


    Deshalb gibt es zwei Möglichkeiten:
    1) Nimm den Autorleasepool, wie von MarkusMüller erklärt
    2) Speicher dir Zeiger auf die Objekte zwischen.

    Quellcode

    1. MyObject* object = [[MyObject alloc] init] //x hat nen RC1 durchs alloc
    2. MyObject* forField1 = [[MyObject alloc] init]; //y hat nen RC1 durchs alloc
    3. MyObject* forField2 = [[MyObject alloc] init]; //z hat nen RC1 durchs alloc
    4. [object setField1:forField1] //y hat nen RC2 durch den Retain-Setter
    5. [object setField2:forField2] //z hat nen RC2 durch den Retain-Setter
    6. [forField1 release]; //y hat nen RC1 durchs Release
    7. [forField2 release]; //z hat nen RC1 durchs Release
    8. [object setField1:[object field2]] //x RC1, y RC0 durchs Release im Setter, z RC2 durch zwei Setter
    9. [object release]; // x RC0 durchs Release, y RC0 durchs dealloc von x, z RC0 durchs dealloc von x


    So kommst du zu jeder Zeit ans Objekt ran. Du kannst gern nach dem Release der zwischengespeicherten Zeiger diese auch auf 'nil' setzen, um dich warnen zu lassen solltest du später im Code auf den Zeiger eines freigegebenen Objektes zugreifen wollen.

    Ich empfehle Möglichkeit 1. Die Releases vergisst du. ;)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • So wie ich das sehe ist es falsch und HansGerber hat mit seiner Vermutung recht.

    Gute Güte, wenn sich schon die Experten uneins sind, wie soll ich da je auf einen grünen Zweig kommen, äh, das ganze kapieren.
    Andererseits beruhigt es irgendwie, bin ich doch nicht alleine ....


    MyObject* object = [[MyObject alloc] init] //x hat nen RC1 durchs alloc
    [object setField1:[[MyObject alloc] init]] //y hat nen RC1 durchs alloc, RC2 durch den Retain-Setter
    [object setField2:[[MyObject alloc] init]] //z hat nen RC1 durchs alloc, RC2 durch den Retain-Setter
    [object setField1:[object field2]] //x RC1, y RC1 durchs Release im Setter, z RC3 durch den zweiten Setter
    [object release]; // x RC0 durchs Release, y bleibt bei RC1, z RC1 durchs Release von field1 und field2
    Nach [object release], wenn also rc = 0 wird, wird doch dealloc aufgerufen.
    Wenn in diesem dealloc object.field1(2) = nil gesetzt wird, setzt dann nicht der Setter den rc durch Freigabe auf 0 ??



    Mit verwirrtem Gruss ! 8|
    Hans