memory leak

  • Du erzeugst die NSImageRep-Instanz ja mit einem Convenience Allocator, d.h. die Instanz ist autoreleased und darf daher nicht noch einmal released werden. Solche autoreleasten Objekte wirst Du nur dann schneller los, wenn Du das ganze in einen lokalen Autoreleasepool "einpackst".

    Quellcode

    1. - (NSArray*) imageResolution: (NSString*) path
    2. {
    3. NSAuroreleasePool *pool = [[NSAutoreleasePool alloc] init];
    4. NSImageRep * myImageRep = [NSImageRep imageRepWithContentsOfFile:path];
    5. NSString *w = [NSString stringWithFormat:@"%0.d",[myImageRep pixelsWide]];
    6. NSString *h = [NSString stringWithFormat:@"%0.d",[myImageRep pixelsHigh]];
    7. NSString *r = [NSString stringWithFormat:@"%0.lf",(([myImageRep pixelsHigh] * 72) / [myImageRep size].height)];
    8. NSString *s = [NSString stringWithFormat:@"%0.2f",([[[fileManager fileAttributesAtPath:path traverseLink:YES] objectForKey:NSFileSize] floatValue] / 1024 / 1024)];
    9. [pool release];
    10. return [NSArray arrayWithObjects:w,h,r,s,nil];
    11. }
    Alles anzeigen

    Michael
  • Das kann auch nicht funktionieren, weil der release zu führ kommt. Schauen wir uns das mal genau an:

    Er legt einen Autoreleasepool an. Richtig so! In diesem ARP legt er ARP-Objekte an. Richtig so! Alle diese Objekte haben jetzt einen RC von 1, weil sie vom ARP einmalig gehalten werden.

    Dann gibt er den ARP frei. Dieser gibt hierbei alle gehaltenen Objekte frei. Die nächste Anweisung kommt zu spät. Das Array retaint objekte, die bereits weg sind.

    Nimm einfach die Freigabe des ARP eine Zeile tiefer, nach dem NSArray ... Dann müsste es klappen.
    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"?
  • erst einmal: Anweisungen nach einem return werden nicht ausgeführt.
    Ansonsten: Da du nur "convinience methods" verwendest, kannst du dir den lokalen ARP auch sparen. Die Objekte werden dann nur etwas später (beim Eintritt in die main run loop) freigegeben).
    cu
    Peter
  • Arrgh. Man sollte nicht unter Zeitdruck programmieren. Die ganzen Strings landen natürlich auch in dem lokalen Autroreleasepool und sind dann natürlich auch weg. Die Lösung ist aber doch ganz einfach. Den Strings braucht man nur noch ein zusätzliches -retain schicken, dann überleben sie das Leeren des Autoreleasepools. Alternativ kann man das auch noch so machen, was dann auch "sauberer" ist:

    Quellcode

    1. - (NSArray *)imageResolution:(NSString *)path
    2. {
    3. int high, wide;
    4. NSSize aSize;
    5. NSAuroreleasePool *pool = [[NSAutoreleasePool alloc] init];
    6. NSImageRep *myImageRep = [NSImageRep imageRepWithContentsOfFile:path];
    7. aSize = [myImageRep size];
    8. high = [myImageRep pixelsHigh];
    9. wide = [myImageRep pixelsWide];
    10. [pool release];
    11. NSString *w = [NSString stringWithFormat:@"%0.d", wide];
    12. NSString *h = [NSString stringWithFormat:@"%0.d", high];
    13. NSString *r = [NSString stringWithFormat:@"%0.lf", ((high * 72) / aSize.height)];
    14. NSString *s = [NSString stringWithFormat:@"%0.2f", ([[[fileManager fileAttributesAtPath:path traverseLink:YES] objectForKey:NSFileSize] floatValue] / 1024 / 1024)];
    15. return [NSArray arrayWithObjects:w, h, r, s, nil];
    16. }
    Alles anzeigen

    Michael
  • Bitte lies meinen Beitrag in der Wiki. Die Sache ist ganz einfach. Ihr fangt jetzt an herumzustochern.

    Man muss niemals außerhalb eines setters einen -retain schicken. Das ist Grundfalsch.

    Sorge einfach dafür, dass das NSArray die Chance zum retainen hat, bevor du den Pool freigibst.

    Nichts anderes machen!

    Wenn das nicht funktionieren sollte, verspreche ich, es nachzuprgrogrammieren und nachzuschauen.

    Aber bite nicht planlos herumfummeln!
    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"?
  • Original von Tom9811
    Argh Nein! UM GOTTES WILLEN!

    Grrrr, heute ist nicht mein Tag. Ich geh' gleich ins Bett :(
    Den Strings ein -retain zu schicken ist natürlich Blödsinn. Da fehlt dann ja das dazu gehörige -release. Den Autroreleasepool aber nach der return-Anweisung zu releasen bringt es auch nicht. Da kommt das Programm nie hin. ;)
    Die zweite Variante ist aber in Ordnung.

    Michael
  • Doch, das ist die Lösung. Sorry, ich hatte gesehen, dass er das Array im return baut.

    Also:

    Quellcode

    1. theArray = [NSArray arrayWithObjects: ....];
    2. [pool release];
    3. return theArray;


    Ich habe es gerade probiert. Es funktioniert. Nebenbei muss es auch funktionieren. Nur noch zur Erläuterung.

    1. Der ARP ist ein Container. Stell ihn dir wie ein Array vor. Jedes Objekt, welches im ARP landet hat einen RC von 1. Wird der ARP geleert, so gibt es ein release und dsa Objekt fliegt heraus.
    Das dürfte klar sein.

    2. Das Array ist ein Container. Wenn du ein Objekt hinzufügst, wird es retaint. Der RC erhöht sich um eins.
    Das dürfte klar sein.

    3. Wenn dem so ist, so kannst du den ARP herauswerfen,, wenn du es im Array hast. Denn durch das Zerstören des ARP wird der RC dekrementiert. Da es aber _vorher_ in dem Array ist, hat es noch einen ARP von 1.
    Alle anderen Objekte im ARP hingegen werden gelöscht.

    4. Nochmal: JEDER KÜMMMERT SICH UM SEIN OBJEKT!
    Wenn das Array das Objekt halten will, soll das Array ein retain schicken, was es auch macht.
    Wenn niemand anderes das Objekt halten will, landet es eben im ARP.
    Wenn das Objekt gar niemand halten wollte ist es damit demnächst weg.
    Wenn es jemand halten wollte, behält er es auch.

    Es ist ganz einfach:
    JEDER KÜMMERT SICH UM SEIN OBJEKT!

    Klar? Ich werde aber noch etwas im Artikel dazu schreiben.
    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"?
  • Original von Tom9811
    Doch, das ist die Lösung. Sorry, ich hatte gesehen, dass er das Array im return baut.

    Also:

    Quellcode

    1. theArray = [NSArray arrayWithObjects: ....];
    2. [pool release];
    3. return theArray;


    Ich habe es gerade probiert. Es funktioniert.

    Nein, nein, nein. Das ist äußerst gefährlich und dass es bei Dir funktioniert reine Glückssache. Denn denk an Deine eigenen Worte. ;) Du erzeugst theArray hier mit einem convenience allocator, d.h. theArray wird dem Autoreleasepool hinzugefügt, den Du anschließend gleich released. Somit ist theArray bereits ungültig und das return gibt ein äußerst fragwürdiges theArray zurück. Schließlich hat NSArray ebenfalls einen retain counter.
    So würde es sicher gehen:

    Quellcode

    1. theArray = [[NSArray alloc] initWithObjects: ....];
    2. [pool release];
    3. return [theArray autorelease];
    Auf diese Weise landet theArray im globaleren Autoreleasepool.

    Michael
  • Nein, das ist nicht gefährlich. Ein Objekt befindet sich _nicht im_ ARP, sondern wird durch diesen _verlinkt_. Ein ARP ist ein normaler Container. Ein Löschen des ARP führt nur dann zum Löschen des Objektes, wenn der ARP _der einzige Referenzierer_ des Objektes ist.

    Aber: Jetzt merke ich langsam, w>ARUM DAS ALLE ANDERS MACHEN UND WO DAS Missverständnis liegt. Kay soll endlich das Hochladen aktiviren, damit man das mal aufzeichnen kann.
    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"?
  • Nein, bei einem CA im allgemeinen ARP werden die Objekte erst am Ende der RL freigegeben.

    Bei einem "eigenem" ARP, werden sie bereits freigegeben, wenn man den ARP freigibt.

    Das ist bei einer Widerholung ein Riesenunterschied!

    macentwicklerwelt.net/index.ph…_Gro.C3.9F_wachsender_ARP
    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"?
  • Sorry, hate das nicht richtig gelesen. Das Array muss freilich vor dem pool erzeugt werden. Das kann man in dem Ausschnitt freilich nicht sehen. :(

    Schau mal auf den Beitrag in der Wiki. Da habe ich mal ein Beispiel gepostet.
    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"?
  • Original von Tom9811
    Nein, das ist nicht gefährlich.

    Doch :]

    Original von Tom9811
    Ein Objekt befindet sich _nicht im_ ARP, sondern wird durch diesen _verlinkt_.

    Ja, ich weiß. Ich werde das zukünftig sprachlich korrekt ausdrücken.

    Original von Tom9811
    Ein ARP ist ein normaler Container. Ein Löschen des ARP führt nur dann zum Löschen des Objektes, wenn der ARP _der einzige Referenzierer_ des Objektes ist.

    Ein Objekt wird doch gelöscht, wenn der retain counter auf 0 geht. So, wenn Du nun hier im Kontext eines lokalen ARP ein Array mit einem CA erzeugst, dann ist
    1. der retain counter des Arrays = 1
    2. der lokale ARP hat eine Referenz auf dieses Array
    3. wird der lokale ARP freigegeben, schickt dieser ein -release an das Array -> retain counter wird zu 0
    4. das Array schickt ebenfalls -release-Messages an seine referenzierten Objekte.
      [/list=1]
      Sollte dem nicht so sein, dann hat NSArray wohl eine Sonderstellung im Cocoa-Speichermanagement.

      Michael
  • Ja, die zweite Antwort habe ich erst gesehen, als ich meinen Beitrag schon abgeschickt hatte.
    Vor dem Pool kann das Array in diesem speziellen Fall aber nicht mit -arrayWithObjects: angelegt werden, weil die benötigten Objekte ja erst nach dem Pool erzeugt werden. Da müsste man dann mit einem NSMutableArray arbeiten.

    Michael
  • Jep, das stimmt. Ich hatte das mehr allgemein geschireben. Den ganzen Code hier im konkretem Falle habe ich ohnehin nicht im Kopf, da ich mich ja lange nicht dern Diskussion beteiligt habe.

    Das ist hier in der Tat etwas unschön, weil er ja die genae Anzahl der Objekte kennt. (Wobei ich mich ohnehin wundere, dass er ein Array nimmt. Das gibt noch Ärger. Ein Dictionary wäre angebrachter.)

    Man kann hier natürlich daran denken, dass man dem Array (nur dem Array) ein retain schickt, so dass der [pool release] dies auf 1 zählt. Nicht wirklich schön, aber gangbar. Eine deutliche KOmmentierung sollte auf jedem Falle vorgenommen werden.
    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"?