Frage zu dealloc und release

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

  • Frage zu dealloc und release

    Hallo,

    ich bin gerade bei der Programmierung für Mac OS X. Dabei hätte ich mal zwei Fragen, warum folgender Code funktioniert, obwohl er meiner Meinung nach den 2. NSLog nicht machen dürfte.
    Ich arbeite übrigens ohne ARC.
    1)

    Quellcode

    1. #import <Foundation/Foundation.h>
    2. #import "Zinsrechner.h"
    3. int main(int argc, const char * argv[])
    4. {
    5. @autoreleasepool {
    6. Zinsrechner *meinZinsrechner = [[Zinsrechner alloc] init];
    7. double kontostand = 100;
    8. double zinssatz = 5.0;
    9. double laufzeit = 5;
    10. double d = [meinZinsrechner berechneZinsen:kontostand : zinssatz : laufzeit];
    11. NSLog(@"Kontostand %.2f", d);
    12. [meinZinsrechner dealloc]; //Gibt den Speicher vom Objekt meinZinsrechner wieder frei.
    13. double x = [meinZinsrechner berechneZinsen:100 :4.00 :5];
    14. NSLog(@"%.2f", x);
    15. }
    16. return 0;
    17. }
    Alles anzeigen


    So wie ich das verstanden habe, gibt dealloc das Objekt ohne Rücksicht auf Verluste wieder Frei. Also unabhängig von retain-Zähler. Somit dürfte doch der 2. NSLog nicht mehr funktionieren.

    2) Mit jedem mal wo ich das Objekt verwende, wird der retain-Zähler ja um 1 erhöht. Wenn ich schreibe [meinObjekt release] setze ich den retain-Zähler ja um 1 zurück.
    Dies sollte ich ja nach jeder Verwendung des Objektes machen. Wenn retain-Zähler == 0 wir das Objekt ja gelöscht.
    Jetzt aber meine Frage:
    Wenn ich das Objekt das erste mal erstelle hat der Retain-Zähler ja den wert von 1.
    Wenn ich jetzt das objekt 10 x verwende und 10 x danach den release-Befehl ausführe hat der retain-Zähler ja dann den Wert 1.
    Somit komme ich ja nur dann auf 0, wenn ich irgendwo 2x hintereinander den relese-Befehl ausführe.
  • Apple123 schrieb:

    So wie ich das verstanden habe, gibt dealloc das Objekt ohne Rücksicht auf Verluste wieder Frei. Also unabhängig von retain-Zähler.

    Und genau deshalb rufst Du dealloc nie selbst auf! Einzige Ausnahme: [super dealloc] in einer dealloc-Implementation, wenn Du ohne ARC arbeitest.

    Apple123 schrieb:

    Somit dürfte doch der 2. NSLog nicht mehr funktionieren.

    Das System gibt den Speicher für das Objekt wieder frei, aber es löscht den Speicher nicht. D.h. wenn der Speicher nicht durch etwas anderes überschrieben wird, liegt an der alten Speicherstelle immer noch das alte Objekt rum. Das 2. NSLog funktioniert daher rein zufällig noch.

    Apple123 schrieb:

    Mit jedem mal wo ich das Objekt verwende, wird der retain-Zähler ja um 1 erhöht.

    Nein, der Retain-Zähler erhöht sich nur, wenn man dem Objekt ein retain schickt. Die reine Verwendung eines Objektes verändert den Retain-Zähler nicht. Wozu auch. Das würde eine funktionierende Speicherverwaltung nahezu unmöglich machen.

    Michael
  • merci, alle meine Fragen sind jetzt beantwortet :)

    Jetzt ist bei mir aber noch eine Frage aufgekommen.
    Und zwar habe ich den Garbage Collector in den "Build Setting" von meinem Projekt gesucht, aber nicht gefunden.
    Wurde mit xCode 4.2 der Garbage Collector gestrichen?

    Gibt es also nur noch Automatic Reference Counting (ARC)?

    Der Garbage Collector ist doch teil von Objective-C und ARC nur eine Funktion von xCode, welche die retain und release Befehle (bei Kompilieren) automatisch setzt.
  • Apple123 schrieb:

    Wurde mit xCode 4.2 der Garbage Collector gestrichen?

    Weiß ich jetzt nicht, ob und/oder wann der Garbage Collector in Xcode gestrichen wurde. Was ich aber weiß ist folgendes:

    Important: Garbage collection is deprecated in OS X v10.8. You should use ARC instead

    Vergiss also den Garbage Collector. Der ist tot und hat eh nie so richtig funktioniert. Unter iOS gab es den erst gar nicht.

    Michael
  • Aja, noch eine Sache vom Anfang ist mir unklar:

    Mir ist jetzt klar, das ich den retain-Zähler nur mit dem Befehl "retain" um eins erhöhe.
    Dies sollte ich ohne ARC ja bei jeder Verwendung des Objektes machen.
    Nachdem das Objekt dann verwendet habe, dekrementiertiere ich mit "release" den Zähler.
    Aber wenn der Zähler bei 1 anfängt (nach dem erstellen des Objektes) wie komme ich dann auf 0?
    Aus 0 komme ich doch nur, wenn ich zwei mal hintereinander den "release"-Befehl ausführe.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Apple123 ()

  • alloc init konstrukte erzeugen implizit "retained" objekte - du mußt sie also nciht noch einmal zusätzlich retainen sondern nur nach verwendung releasen

    dem gegenüber erzeugen CAs "autoreleased" Objekte deren retaincount beginnt sozusagen bei null

    Aber letztlich sollte dich der Count nicht interessieren sondern du solltest eher in Eigentümerschaft denken:

    hab ich das Objekt selbst erzeugt (alloc init)? -> ich muß mich auch ums aufräumen kümmern
    hab ich das Objekt an mich gebunden(retain)? -> ich muß es auch wieder loswerden
    hab ich das Objekt von einem anderem "bekommen" (CAs / FRameworks etc) -> andere kümmern sich ums aufräumen - ich muß das nur tun wenn 2. zutrifft
    snafu
    :() { :|: &};:
    sometimes i dream in hex
    Obey gravity! Because its a law!
  • Okay, danke.

    Eine sache habe ich aber immer noch nicht verstanden.
    Ich habe mir jetzt ein Buch ausgeliehen und arbeite dies gerade durch.
    Dort werden die Getter und Setter Methoden manuel angelegt.
    (ARG ist deaktiviert).

    Ich poste mal den Quellcode:

    Quellcode

    1. #import "Person.h"
    2. @implementation Person
    3. //Alter:
    4. - (int)alter{
    5. return alter;
    6. }
    7. - (void)setAlter:(int)value
    8. {
    9. if (alter != value)
    10. {
    11. if (value > 130) {
    12. alter = 130;
    13. }
    14. else if (value < 0){
    15. alter = 0;
    16. }
    17. else{
    18. alter = value;
    19. }
    20. }
    21. }
    22. //--Name:
    23. - (NSString *)name
    24. {
    25. return [[name retain] autorelease];
    26. }
    27. - (void)setName:(NSString *)value
    28. {
    29. if (name != value) {
    30. [name release];
    31. name = [value copy];
    32. }
    33. }
    34. //Vorname:
    35. - (NSString *)vorname
    36. {
    37. return [[vorname retain] autorelease];
    38. }
    39. - (void)setVorname:(NSString *)value
    40. {
    41. if (vorname != value)
    42. {
    43. [vorname release];
    44. vorname = [value copy];
    45. }
    46. }
    47. @end
    Alles anzeigen


    Was mit jetzt noch unklar ist ist folgendes:

    1) Warum frage ich bei der setter-Methode erst mit einer if-Abfrage ab, ob ob der übergebene Wert ungleich dem aktuellen Wert ist? Ich könnte doch einfach immer den Wert setzten?
    (Auch wenn der eben gleich ist, na und dann ändere ich den Wert eben auf den gleichen Wert).

    2) Warum sehen die Getter und Setter-Methoden bei einem NSString-Objekt so aus:

    Quellcode

    1. //--Name:
    2. - (NSString *)name
    3. {
    4. return [[name retain] autorelease];
    5. }
    6. - (void)setName:(NSString *)value
    7. {
    8. if (name != value) {
    9. [name release];
    10. name = [value copy];
    11. }
    12. }
    Alles anzeigen


    Besonders die getter-Methode ist mir unklar. Warum der retain und danach der autorelease? Was ist der Unterschied zu einem normalen release?

    3) Frage:
    Ist in der Variablen name (vom Typ NSString) jetzt nur die Speicheradresse gespeichert, oder wirklich eine Zeichenkette?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Apple123 ()

  • Ist das der Hinzberg? Dann am besten wieder möglichst schnell zurückgeben.

    Frage 1)
    Hinzberg hat nicht verstanden, warum man das zuweilen macht und macht es hier in der Tat völlig überflüssig. Wirf das if weg. Das Buch auch.

    Übrigens sollte man das if auf 130 auch wegwerfen. Das ist mindestens grenzwertig. Wenn man Bereichsüberprüfungen haben möchte, dann gibt es dafür eine eigene Technologie. Keine Ahnung, ob Hinzberg jemals davon gehört hat. Ich vermute aber mal stark: Nein.

    Frage 2)
    Sollte das nicht das Buch beantworten?
    Der Grund ist einfach:
    Person person = [[Person alloc] init];
    [person setName:…];

    // Sehr viel später
    NSString *name = [person name];
    [person release];

    In der letzten Zeile wird das Person-Objekt weggeworfen. Da es aber der einzige ist, der das String-Objekt hält, wird das String-Objekt im -dealloc von Person ebenfalls weggeworfen. (Muss nicht zwingend passieren, kann aber passieren.) Das solltest du mit dem Buch auch machen. Zwingend.

    Durch das -retain erhält name noch einen Retaincount, der allerdings durch das -autorelease im nächsten Durchgang der Runloop wieder weggenommen wird. Mit anderen Worten: Das Name-Objekt lebt bis zum Ende der RL und obiger Code stürzt dadurch nicht ab.

    Frage 3)
    Nur die Speicheradresse. Die Variable hat auch den Typen NSString *, nicht NSString.
    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"?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Amin Negm-Awad ()

  • Amin Negm-Awad schrieb:

    Frage 2)
    Sollte das nicht das Buch beantworten?
    Der Grund ist einfach:
    Person person = [[Person alloc] init];
    [person setName:…];

    // Sehr viel später
    NSString *name = [person name];
    [person release];

    In der letzten Zeile wird das Person-Objekt weggeworfen. Da es aber der einzige ist, der das String-Objekt hält, wird das String-Objekt im -dealloc von Person ebenfalls weggeworfen. (Muss nicht zwingend passieren, kann aber passieren.) Das solltest du mit dem Buch auch machen. Zwingend.

    Durch das -retain erhält name noch einen Retaincount, der allerdings durch das -autorelease im nächsten Durchgang der Runloop wieder weggenommen wird. Mit anderen Worten: Das Name-Objekt lebt bis zum Ende der RL und obiger Code stürzt dadurch nicht ab.
    Irgendwie verstehe ich 2) immer noch nicht richtig.
    Okay, mit dem [person release]; wird das Personen-Objekt weggeworfen. Darin ist auch der NNString vom Namen (also von der Instanzvariablen) enthalten, die ist dann natürlich auch weg.
    Aber davor habe ich ja gerade mit der Getter-Methode den Namen ausgelesen, noch bevor "release" das Personenobjekt wegschaffen konnte.
    Damit gibt es zwar das Personenobjekt nicht mehr und die Insztanzvariable vom Personen ist weg, aber meinen Namen habe ich trotzdem noch vorher auslesen können.


    Der einzige Weg, wie ich mir das erklären könnte währe, wenn der Rechner den (Kompilierten) Programmcode nicht von Oben nach unten abarbeitet, sondern wenn dies in mehreren Zyklen passieren würde. Ist das zufällig so?
    Dann würde es ja durchaus einen Sinn ergeben, die Instanzvariable des Namens (im Objekt Personen) mit retain noch mal festzuhalten, denn wenn mein Perosnenobjekt (in einem Zyklus) weggeschafft werden würde könnte ich im nächsten mit getName die Variable gar nicht mehr auslesen da es diese dann nicht mehr gibt.



    ps. zu 1 hat Hinzberg übrigens geschrieben, das es in deinem Fall nicht notwendig ist, es aber andere Fälle gibt in denen man ohne die Abfrage zusätzlichen Speicherplatz für den selben value beanspruchen würde.
  • Nein du hast nicht den Namen ausgelesen sondern nur den Zeiger auf den Speicher wo der Name steht. Wird nun der Speicher freigegeben und du versuchst diesen zu lesen, dann kann es sein das er schon anderwertig benutzt wird (Es kann ja sein das in der Zwischenzeit zwischen auslesen des Namens und benutzen dessen ein anderer Thread dran gekommen ist (Multitasking) und der hat Speicher angefordert und diesen bekommen und dann was reingeschrieben)

    Ein NSString ist nur der Zeiger auf einen Speicherbereich. Wenn Du also

    NSString *b=a; // wobei a ein NSString * von irgendwoher ist

    machst, dann steht in b nur der Zeiger auf diesen Speicehr aber nicht der String. Somit hast du auch keine Kopie von dem String angelöegt sondern nur von dem Zeiger darauf. Gibst du also a danach frei, dann zeigt zwar b immer noch auf den gleichen Speicher, in diesem kann aber längst was anderes drin stehen.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Okay, also nehmen wir mal folgendes Beispiel an (wenn ich retain und release bei der Getter-Methode nicht verwändet hätte):

    1) Ich besorge mit den Namen (bzw. die Reverenz auf diesen) aus dem Objekt --> Getter-Methode.

    Quellcode

    1. [meinePerson name]

    2) Ich speichere diesen in einer Variablen.

    Quellcode

    1. NSString *ausgelesenerName = [meinePerson name];

    3) Ich verwerfe das Objekt meinePerson (damit auch das NSString-Objekt).
    3.1) Vielleicht kommt jetzt sogar noch ein anderer Thread.
    4) Irgendwann später möchte ich auf die ausgelesene Variable zugreifen, mein pointer *ausgelesenerName (welchen ich mit der Getter-Methode gefüllt habe) läuft jetzt jedoch ins leere.

    Und um das zu vermeiden erhöhe ich den retain-Zähler vom NSString-Objekt innerhalb des personen-Objektes um 1 (mit dem verwenden der Getter-Methode).
    Fliegt jetzt das Personen-Objekt raus, habe ich immer noch meine NSString-Variable vom Personen-Objekt auch wenn das Objekt person schon weg ist.

    Die einzigste sache, die ich mich jetzt noch frage ist, wie "autorelease" funktioniert. Erkennt "autorelease" wenn ich das Objekt nirgendwo mehr verwende, und setzt dann erst den Zähler um 1 zurück?
  • Ich glaube du solltest dir mal anschauen wie an sich die Abarbeitungsreihenfolgen in einem Programm ineinander greifen.

    Also welche Teile streng Linear durchlaufen werden, an welcher Stelle Schnitte sind oder Loops gestartet werden und wo Nebenläufigkeit auftritt bzw auftreten kann.

    zb kannst du den name-String problemlos innerhalb deines Blocks (also der Methode) benutzen. Willst du ihn über eine Runloopgrenze behalten mußt du ihn wohl retainen (was du ja meist in einer Property oder ähnlichen machen wirst).

    wenn du Threadsicherheit haben möchtest mußt du natürlich auch Threadsichere Klassen und propertys schreiben bzw verwenden - das ist zb ein Grund für das [[foo retain] autorelease]; in deinem Buch.


    autorelease markiert das Objekt als released nur wird das "tatsächliche" release bis zum nächsten drain des AutoreleasePools aufgeschoben.
    snafu
    :() { :|: &};:
    sometimes i dream in hex
    Obey gravity! Because its a law!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von chartus ()

  • @123:

    Nein, automatisch erkennen macht das ARC. Bei dem Autrorelease-Pool (ARP) ist es so, dass bis zum Ende der Runloop gewartet wird. RL? Sollte ja auch in dem Buch erläutert sein. ;-)

    Grob skizziert: In dem Moment, in dem der Nutzer eine Aktion auslöst (Tastendruck, Mausklick) wird ein ARP angelegt und dann die von dir programmierte Methode ausgeführt. Ist diese Methode beendet, so geht es wieder zurück ins Betriebssystem. Kurz vorher wird der ARP gelöscht und alle Objekte, die nur von dem ARP referenziert werden, verschwinden. Ein Objekt mit -autorelease lebt also mindestens so lange, wie die Nutzeraktion abgearbeitet wird. Oder anders: So lange, wie du mit deinem Finger deinem Code folgen kannst.
    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"?