Speicherproblem

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

  • "Marco Feltmann" schrieb:


    AFAIK ist self auch in C# etwas ganz anderes als this. (self bei statischen Dingen und this bei allem Anderen.)

    In C# gibt es gar kein self, nur this :).


    So, ich hab jetzt nochmal meinen Quelltext angeschaut und bin jetzt noch verwirrter als vorher.
    Ich wollte zum einen Mikes Tip ausprobieren und zum anderen 'meine Entdeckung', das self.x nicht das gleiche wie x ist nutzen ;).

    Wenn ich jetzt in der Init-Methode folgendes schreibe:

    Quellcode

    1. self.imageBG1 = [[[UIImage alloc] init] autorelease];

    dann wird doch beim decodieren imageBG1 eh wieder 'überschrieben' oder nicht? Wozu dann die Initialisierung in der Init-Methode?

    Quellcode

    1. Einstellungen* einstellungen = [self init];
    2. einstellungen.imageBG1 = [decoder decodeObjectForKey:dImageBG1];

    Und mit dem Settern und Gettern hab ich ja doch nichts falsch gemacht, da ich die innerhalb der Klasse selbst gar nicht aufrufe oder etwa doch?
    Sorry, vielleicht stell ich mich gerade nur unendlich doof an :).
  • gyrosp schrieb:

    dann wird doch beim decodieren imageBG1 eh wieder 'überschrieben' oder nicht?


    Du musst ja erstmal Speicher für imageBG1 reservieren, das macht der Decoder nicht sondern sollte in deinem Init gemacht werden.

    Quellcode

    1. [self setImageBG1:[[[UIImage alloc] init] autorelease]];


    Weist du was diese Zeilen machen? Ein Tipp will ich mal geben. Unter anderem wird der Retaincount hierbei auf 1 gesetzt. Lies dir auf jeden Fall mal das hier durch: Advanced Memory Management Programming Guide. Hier jetzt wirklich Grundlagen vom Fundament an zu erklären ist IMO für ein Forum zu umfangreich.
    [self setSignature:null];
    [[self postCount] increment];
  • Mike schrieb:

    gyrosp schrieb:

    dann wird doch beim decodieren imageBG1 eh wieder 'überschrieben' oder nicht?


    Du musst ja erstmal Speicher für imageBG1 reservieren

    Nein, das hat gyrosp richtig erkannt. In der Methode initWithCoder: initialisiert man die Instanzvariablen mit den decodierten Objekten aus dem Archiv. Eine Vorabinitialisierung ist nicht notwendig.

    Mike schrieb:

    das macht der Decoder nicht sondern sollte in deinem Init gemacht werden.

    Doch, genau das macht der Decoder und die Methode initWithCoder: ist eine init-Methode.

    Quellcode

    1. [self setImageBG1:[[[UIImage alloc] init] autorelease]];


    Mike schrieb:

    Weist du was diese Zeilen machen? Ein Tipp will ich mal geben. Unter anderem wird der Retaincount hierbei auf 1 gesetzt.

    Ist aber trotzdem überflüssig, denn in initWithCoder: wird ja wieder setImageBG1: aufgerufen, welches dann die UIImage Instanz durch die vom Decoder gelieferte UIImange Instanz ersetzt.

    @gyrosp
    ich würde Dir empfehlen, die Klasse mal ganz neu aufzusetzen. Du sagst ja, das Singleton Patten nutzt Du gar nicht mehr. Wozu brauchst Du also die ganzen Klassenmethoden noch? Das Ganze sieht wie Kraut und Rüben aus. Da steigt man kaum noch durch. Und wegen dem Archivieren lies Dir das hier mal durch.

    Michael
  • Die Load Methode hat definitiv einen Bug da der eine Ausgang der Methode ein "autorelease" Objekt liefert, während der andere ein Objekt liefert, dessen Referenzzähler (retain) explizit um eins hochgezählt wird. Während im ersten Fall der Autoreleasepool das Objekt sauber wegräumt, ist im zweiten Fall natürlich ein explizites release angesagt. Da du aber nicht weißt was zu tun ist, gibt es entweder einen crash wegen eines release auf dem "autorelease" Objekt oder es gibt ein Speicherloch.

    Zur Lösung deines Problems solltest Du zuerst mal Load reparieren und das retain entfernen. Danach liefert die Methode in beiden Fällen ein "autorelease" Objekt. Wenn Du dann Teile des Objektes brauchst musst Du deinen Bedarf durch ein retain anmelden. Sollte bzImage ein Property sein, dann sollte das der Setter dieses Properties erledigen. Natürlich muss er das Objekt welches er bisher gehalten hat auch freigeben, aber das setter Pattern sollte dir bekannt sein sofern Du es nich eh den Compiler generieren lässt. In deinem Fall solllte es wohl eines mit retain Attribut sein. Ist es kein Property, dann musst Du dich selbst drum kümmern.

    Quellcode

    1. /* Erstmal ein retain aufs imageBG1 damit wir es nicht freigeben wenn imageBG1 == bzImage ist*/
    2. [einstellungen.imageBG1 retain];
    3. /* Dann das alte bzImage freigeben */
    4. [bzImage release];
    5. /* und zuweisen. */
    6. bzImage = einstellungen.imageBG1;


    Ist es ein Property, dann sieht es einfach ein

    Quellcode

    1. self.bzImage = einstellungen.imageBG1;
  • Michael schrieb:

    Nein, das hat gyrosp richtig erkannt. In der Methode initWithCoder: initialisiert man die Instanzvariablen mit den decodierten Objekten aus dem Archiv. Eine Vorabinitialisierung ist nicht notwendig.

    Und wie sollen die da rein gekommen sein? ;)
    Beim ersten Programmstart gabs ja nix zu laden, wie wurde da die Property initialisiert? ;)

    EDITH:

    Ich hab bestimmt noch was übersehen aber IMO müsste Einstellungen eher so aussehen:

    Quellcode

    1. //
    2. // Einstellungen.m
    3. #import "Einstellungen.h"
    4. #import "Einstellungen.h"
    5. #define dEinstellungen @"Einstellungen"
    6. #define dImageBG1 @"imageBG1"
    7. //... weitere Definitionen
    8. @implementation Einstellungen
    9. @synthesize imageBG1;
    10. //... weitere Properties synthetisieren.
    11. static Einstellungen* sharedInstance = nil;
    12. + (Einstellungen*)sharedInstance
    13. {
    14. if (sharedInstance == nil)
    15. {
    16. sharedInstance = [[super allocWithZone:NULL] init];
    17. }
    18. return sharedInstance;
    19. }
    20. +(NSString*)GetFilePath
    21. {
    22. NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    23. NSString* pathWithFileName = [[path objectAtIndex:0] stringByAppendingPathComponent:EINSTELLUNGENFILENAME];
    24. return pathWithFileName;
    25. }
    26. #pragma mark -
    27. #pragma mark laden&speichern
    28. -(void)Save
    29. {
    30. NSString* pathWithFileName = [Einstellungen GetFilePath];
    31. NSMutableData *data = [[NSMutableData alloc] init];
    32. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    33. [archiver encodeObject:self forKey:dEinstellungen];
    34. [archiver finishEncoding]; //Mike: braucht man IMO nicht
    35. NSError* err = nil;
    36. [data writeToFile:pathWithFileName options:NSDataWritingAtomic error:&err];
    37. //Mike: wozu den NSError wenn du ihn nicht auswertest? Das nenn ich mal ein kreatives NIL.
    38. [archiver release];
    39. [data release];
    40. }
    41. +(Einstellungen*)Load
    42. {
    43. NSString* pathWithFileName = [Einstellungen GetFilePath]; //Mike: Geht auch, ich hätte hier aber wohl das self benutzt
    44. BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pathWithFileName];
    45. if (fileExists)
    46. {
    47. NSData *codedData = [[[NSData alloc] initWithContentsOfFile:pathWithFileName] autorelease];
    48. if (codedData != nil)
    49. {
    50. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:codedData];
    51. Einstellungen* einstellungen = [unarchiver decodeObjectForKey:dEinstellungen];//Mike: retainen überlassen wir hier mal anderen
    52. [unarchiver finishDecoding]; //Mike: braucht man IMO micht
    53. [unarchiver release];
    54. return einstellungen;
    55. }
    56. }
    57. return [[[Einstellungen alloc] init] autorelease];
    58. }
    59. #pragma mark -
    60. #pragma mark NSCoding
    61. -(void)encodeWithCoder:(NSCoder *)encoder
    62. {
    63. [encoder encodeObject:self.imageBG1 forKey:dImageBG1]; //Mike: self.image.BG1 statt imageBG1
    64. //... weitere Properties encodiere
    65. }
    66. -(id)initWithCoder:(NSCoder *)decoder
    67. {
    68. if(![super init] return nil; //Mike: erstmal Mama initialsieren
    69. self.imageBG1 = [decoder decodeObjectForKey:dImageBG1];
    70. //... weitere Properties decodieren
    71. return self;
    72. }
    73. #pragma mark -
    74. #pragma mark init&dealloc
    75. - (id)init
    76. {
    77. self = [super init];
    78. if (self)
    79. {
    80. self.imageBG1 = [[[UIImage alloc] init] autorelease]; //Mike: Speicherplatz reservieren ist auch nicht schlecht, vor allem wenn nix geladen wurde
    81. }
    82. return self;
    83. }
    84. -(void)dealloc
    85. {
    86. [super dealloc];
    87. self.imageBG1 = nil; //Mike: und wieder self.imageBG1…und releasen macht man nur wenn man selbst retained hat, sonst setzt mal NIL und lässt die Speicherverwaltung arbeiten
    88. //... weitere Properties releasen
    89. }
    90. @end
    Alles anzeigen
    [self setSignature:null];
    [[self postCount] increment];

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von Mike ()

  • hm ich versteh nicht so richtig warum du so auf dem init rumreitest? nil ist was schönes das macht nichts kaputt

    und er propagiert ja die propertys nach aussen und muss so oder so ein UIImage setzen - ich versteh nicht warum da ein leeres vorher angelegt werden soll


    was mir aber so aufgefallen ist das du den dealloc zuerst an super schickst bevor du deine eigen propertys zerstörst - das ist imho recht zweischneidig und eher zu vermeiden...



    an sich denke ich, das seine "Einstellung" Klasse zwar schlimm aussieht aber nicht ursächlich für sein oben benanntes problem ist.
    er wird vermutlich bei der Verwendung der klasse und ihren 'returns' genau so auf Verwendung der Setter verzichtet haben und damit auch auf die "automatische" Speicherverwaltung - dann hat er vermutlich, weil es ihm das Bild unterm Hintern wegezogen hat, mit einem Wilden retain in der Einstellung-Klasse das versucht zu beheben und so ein wachsendes leak produziert...
    snafu
    :() { :|: &};:
    sometimes i dream in hex
    Obey gravity! Because its a law!
  • Also erstmal nochmals vielen Dank an alle, das ist hier echt ein super Forum :).

    @Snoxxi:
    Dein Post hat mir einiges klar gemacht. Ich werde meinen Code nachher mal umschreiben und bin guter Dinge, dass ich das Problem jetzt erkannt habe :).
    Ich denke ich verwende tatsächlich die Zuweisung außerhalb der Klasse Einstellungen falsch und werde prüfen, ob ich die Properties richtig benutze.

    Als nächtes werde ich die dann noch die Klasse Einstellungen an sich aufräumen, das ist mittlerweile echt ein Wildwuchs und sieht aus wie Kraut und Rüben :).


    Eine Verständnissfrage hätte ich noch ;):

    "Mike" schrieb:


    self.imageBG1 = nil; //Mike: und wieder self.imageBG1…und releasen macht man nur wenn man selbst retained hat, sonst setzt mal NIL und lässt die Speicherverwaltung arbeiten
    //... weitere Properties releasen

    Ich hab gelesen, dass man Alternativ auch die Variable (ohne Setter) direkt releasen kann, also:

    Quellcode

    1. [imageBG1 release];

    Geht beides oder ist eins falsch?
  • gyrosp schrieb:

    Also erstmal nochmals vielen Dank an alle, das ist hier echt ein super Forum :).

    @Snoxxi:
    Dein Post hat mir einiges klar gemacht. Ich werde meinen Code nachher mal umschreiben und bin guter Dinge, dass ich das Problem jetzt erkannt habe :).
    Ich denke ich verwende tatsächlich die Zuweisung außerhalb der Klasse Einstellungen falsch und werde prüfen, ob ich die Properties richtig benutze.

    Als nächtes werde ich die dann noch die Klasse Einstellungen an sich aufräumen, das ist mittlerweile echt ein Wildwuchs und sieht aus wie Kraut und Rüben :).


    Eine Verständnissfrage hätte ich noch ;):

    "Mike" schrieb:


    self.imageBG1 = nil; //Mike: und wieder self.imageBG1…und releasen macht man nur wenn man selbst retained hat, sonst setzt mal NIL und lässt die Speicherverwaltung arbeiten
    //... weitere Properties releasen

    Ich hab gelesen, dass man Alternativ auch die Variable (ohne Setter) direkt releasen kann, also:

    Quellcode

    1. [imageBG1 release];

    Geht beides oder ist eins falsch?


    wenn imageBG1 deine ivar ist (könnte aber auch _imageBG1 sein).

    aber dazu gibts mehrere threads über vor und anchteile davon.
  • "gritsch" schrieb:


    wenn imageBG1 deine ivar ist (könnte aber auch _imageBG1 sein).

    aber dazu gibts mehrere threads über vor und anchteile davon.

    Danke, werde dann hier gleich mal etwas suchen :).



    Ich hab gerade folgenden Link überflogen: Archives and Serializations Programming Guide
    und da stellt sich mir folgende Frage:

    Im Link ist folgendes Codebeispiel:

    Quellcode

    1. - (id)initWithCoder:(NSCoder *)coder {
    2. self = [super init];
    3. if (self) {
    4. _firstName = [coder decodeObjectForKey:ASCPersonFirstName];
    5. _lastName = [coder decodeObjectForKey:ASCPersonLastName];
    6. _height = [coder decodeFloatForKey:ASCPersonHeight];
    7. }
    8. return self;
    9. }
    Alles anzeigen


    Mike hat folgenden Code gepostet:

    Quellcode

    1. -(id)initWithCoder:(NSCoder *)decoder
    2. {
    3. if(![super init] return nil; //Mike: erstmal Mama initialsieren
    4. self.imageBG1 = [decoder decodeObjectForKey:dImageBG1];
    5. //... weitere Properties decodieren
    6. return self;
    7. }


    Mike verwendet also den Setter und das Apple-Beispiel weist direkt der ivar zu. Was ist denn nun richtig?
    Ich hätte ja auf die Setter getippt aber dann würde es Apple ja nicht anders machen oder wie muss ich das jetzt verstehen ?(
  • Mike schrieb:

    Michael schrieb:

    Nein, das hat gyrosp richtig erkannt. In der Methode initWithCoder: initialisiert man die Instanzvariablen mit den decodierten Objekten aus dem Archiv. Eine Vorabinitialisierung ist nicht notwendig.

    Und wie sollen die da rein gekommen sein? ;)

    Na, die wurden ja irgendwann mal archiviert. initWithCoder: wird ja durch das Auspacken eines Archivs mittels NSKeyedUnarchiver aufgerufen. Und falls es einen Key im Archiv nicht gibt, wir nil zurückgegeben.

    Mike schrieb:

    Beim ersten Programmstart gabs ja nix zu laden, wie wurde da die Property initialisiert? ;)

    In dem Fall wird aber initWithCoder: auch nicht aufgerufen.

    Michael
  • gyrosp schrieb:

    Mike verwendet also den Setter und das Apple-Beispiel weist direkt der ivar zu. Was ist denn nun richtig?

    Schwierige Frage. Ich bevorzuge im Init wie auch in den Settern einen direkten Zugriff auf die Instanzvariable.
    Eigentlich sollte es egal sein, da Änderungen der Setter in der Subklasse sich nicht auf das Init-Verhalten in der Superklasse auswirken sollten.
    Aber wenn doch, was dann? ;)
    «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
  • Marco Feltmann schrieb:

    gyrosp schrieb:

    Mike verwendet also den Setter und das Apple-Beispiel weist direkt der ivar zu. Was ist denn nun richtig?

    Schwierige Frage. Ich bevorzuge im Init wie auch in den Settern einen direkten Zugriff auf die Instanzvariable.
    Eigentlich sollte es egal sein, da Änderungen der Setter in der Subklasse sich nicht auf das Init-Verhalten in der Superklasse auswirken sollten.
    Aber wenn doch, was dann? ;)


    Mhh, aber sollte die setter-Methode nicht für die korrekte Speicherverwaltung zuständig sein, bzw. das Objekt automatisch releasen, bzw. retainen?
    Wenn ich direkt die ivar zuweise zerschieß ich mir unter Umständen doch wieder die Speicherverwaltung oder nicht?

    Oder anders gesagt, müsste es ohne setter dann nicht eigentlich wie folgt aussehen:

    Quellcode

    1. _firstName = [[coder decodeObjectForKey:ASCPersonFirstName] retain];
  • gyrosp schrieb:

    Oder anders gesagt, müsste es ohne setter dann nicht eigentlich wie folgt aussehen:

    Quellcode

    1. _firstName = [[coder decodeObjectForKey:ASCPersonFirstName] retain];

    Ja, wenn Du die manuelle Speicherverwaltung benutzt.
    Nein, wenn Du ARC benutzt.
    Apple benutzt ARC. Ich benutze ARC.
    «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
  • Marco Feltmann schrieb:

    gyrosp schrieb:

    Oder anders gesagt, müsste es ohne setter dann nicht eigentlich wie folgt aussehen:

    Quellcode

    1. _firstName = [[coder decodeObjectForKey:ASCPersonFirstName] retain];

    Ja, wenn Du die manuelle Speicherverwaltung benutzt.
    Nein, wenn Du ARC benutzt.
    Apple benutzt ARC. Ich benutze ARC.


    Ok, dann passt es wieder :). Ich benutze normalerweise auch ARC aber ich schreib gerade ein update für eine alte app, die leider noch kein ARC benutzt ;).
    Das ist ja auch Kernthema dieses Threads, mit ARC hätte ich das Problem so ja erst gar nicht ;).
  • chartus schrieb:

    hm ich versteh nicht so richtig warum du so auf dem init rumreitest? nil ist was schönes das macht nichts kaputt

    Weil ich u.a. der Meinung bin, dass erstmal die Klasse Einstellungen sauber sein sollte bevor man weiter macht. Stellen wir uns doch mal vor man startet das Programm so das erste Mal auf einem Rechner. Da die Instanzvariablen alle nicht initialisiert werden (sind die dann nicht nil?) hat der encodeWithCoder nie was zu tun. Daraus folgt, das bei folgenden Programmstarts auch der initWithCoder praktisch nix zu tun hat da ja alle Schlüssel auch nil sind.
    However, ich finde die Klasse sollte erst OK sein, dann fixt man die Load-Methode und es gibt keinen Stress mehr. Meine Meinung.
    [self setSignature:null];
    [[self postCount] increment];
  • So ganz verstehe ich das Problem mit dem nil auch nicht...
    Der -initWithCoder wird ja erst aufgerufen, wenn bereits ein Objekt via -encodeWithCoder serialisiert wurde.
    Zwingend notwendige Variablen müssen zu dem Zeitpunkt also vergeben sein.
    Und zwar nicht mit irgendwelchem Blödsinn wie einem leeren Image (ich lege Speicherplatz an, den kein Mensch braucht, nur um ihn später wegzuwerfen?)
    Wenn man so etwas macht, dann ist diese Variable optional und kann auch nil bleiben. (Und wenn sie fast immer nil bleibt muss man sich überlegen, ob man die Variable überhaupt benötigt.)

    Das Schöne ist ja, dass es einem völlig egal sein kann, wie viel -initWithCoder zu tun hat.
    «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