1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Buch: Objective C 2.0 Thema: Speicherlecks (Verständnissprobleme))

Dieses Thema im Forum "iOS-Developer" wurde erstellt von Drobs, 21.09.09.

  1. Drobs

    Drobs Carola

    Dabei seit:
    23.05.08
    Beiträge:
    115
    Also ich bin grade dabei mich durch das Buch Objective C 2.0 von Stephen Kochan durchzuarbeiten, habe jetzt allerdings an einer Stelle arge Verständnissprobleme.

    Und zwar geht es um einen Quelltext für eine Signum-Funktion. Das alles dreht sich um eine Klasse für gebrochene Zahlen (Fraction).
    Hier mal der Quellcode um den es geht:

    Code:
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        Fraction *aFraction = [[Fraction alloc] init];
    	Fraction *sum = [[Fraction alloc] init], *sum2;
    	int i, n, pow2;
    	
    	[sum setTo: 0 over: 1]; //setzt den ersten Bruch auf 0
    	
    	NSLog(@"Gib einen Wert für n ein:");
    	scanf("%i", &n);
    	
    	pow2 = 2;
    	for (i = 1; i <= n; i++) {
    		[aFraction setTo: 1 over: pow2];
    		sum2 = [sum add: aFraction];
    		[sum release];
    		sum = sum2;
    		pow2 *= 2;
    	}
    	
    	NSLog(@"Blabla Ausgabe %g", [sum convertToNum]); //gibt den Bruch als Dezimalzahl aus 
    	[aFraction release];
    	[sum release];
    	
        [pool drain];
        return 0;
    }
    Und die methode add:
    Code:
    -(Fraction *) add: (Fraction *) f
    {
    	Fraction *result = [[Fraction alloc] init];
    	int resultNum, resultDenom;
    	
    	resultNum = (numerator * f.denominator) + (f.numerator * denominator);
    	resultDenom = denominator * f.denominator;
    	
    	[result setTo:resultNum over:resultDenom];
    	[result reduce]; //kürzt den Bruch
    	
    	return result;
    }
    Nun zu dem, was meine Frage hervorbringt, dass was er dazu geschrieben hat:

    Nun meine Verständnissfragen:

    a) Wenn ich das Ergebnis einfach in sum schreiben würde, warum würde ein Speicherleck entstehen? Wird das Objekt nicht einfach überschrieben? Und warum ist das anders, wenn ich es in sum2 überschreibe?

    b) Warum muss am Ende sum2 nicht auch freigegeben werden?

    Ich hoffe mir kann das einer erklären, vor allem da mir das doch relativ wichtig erscheint.
     
  2. gKar

    gKar Maunzenapfel

    Dabei seit:
    25.06.08
    Beiträge:
    5.362
    Nun: Ohne in die Referenz geguckt zu haben, erzeugt nach diesem Code zu urteilen die add-Methode von Fraction ein neues Fraction-Objekt (mit Retain-Count 1). Das alte Objekt (von sum referenziert) muss freigegeben werden, und daher ist es natürlich notwendig, das neue Objekt vorerst mit einem anderen Zeiger zu referenzieren, denn wenn man sum einfach überschriebe, hätte man ja keinen Zeiger mehr auf das alte Objekt und könnte es somit nicht mehr wieder freigeben.
    Nachdem dann sum released wurde, kann der Zeiger sum anschließend wieder auf das neue Fraction-Objekt umgebogen werden (sum=sum2;).

    Ohne große Objective-C-Erfahrung zu haben, erscheint mir das offensichtlich. Das sähe selbst mit einfacher Speicherverwaltung à la Pascal (ohne Retain-Counts) nicht anders aus.
     
  3. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Das liegt daran, dass er bei der Objekterzeugung mit +alloc-init-Crap arbeitet.
    Code:
    Fraction *sum = [[Fraction alloc] init
    Das macht man zumindest auf dem Desktop schon seit Jahren nicht mehr.

    Ich hoffe mal, dass das ein Beispiel dafür sein soll, wie man es möglichst nicht macht.
     
  4. Tekl

    Tekl Fairs Vortrefflicher

    Dabei seit:
    01.06.05
    Beiträge:
    4.622
    Als außenstehender würde mich nun interessieren, warum man das auf dem Desktop nicht mehr macht und anderswo doch noch, vor allem wo? iPhone?
     
  5. Thaddäus

    Thaddäus Golden Noble

    Dabei seit:
    27.03.08
    Beiträge:
    15.163


    Wundert mich, dass du antwortest, wo er doch das Buch der Konkurrenz gekauft hat... :p :p
     
  6. Tekl

    Tekl Fairs Vortrefflicher

    Dabei seit:
    01.06.05
    Beiträge:
    4.622
    Das war doch versteckte Werbung für sein Buch. ;)
     
  7. Drobs

    Drobs Carola

    Dabei seit:
    23.05.08
    Beiträge:
    115
    Ist auch schwer ein Buch zu kaufen, von dem ich gestern das erste mal gehört habe.

    Vllt. habe ich das Glück und die ominöse "bessere" Methode wird später noch angesprochen. Und wenn nicht, dann hoffe ich einfach mal auf den Hillegass.

    @gKar
    Danke, deine Erklärung hat mir das Verständniss doch wesentlich erleichtert.
     
  8. Jamsven

    Jamsven London Pepping

    Dabei seit:
    21.11.07
    Beiträge:
    2.046
    Ich denke er meint den convenience allocator
    Wobei in dem konkreten Beispiel keine Event-Loop existiert und daher der AutoreleasePool kurz vor dem main return geleert wird.
    Nun müsste man seinen eigenen ARP definieren und diesen sinnvoll leeren.
    Der ARP ist nichts anderes als der Typ in der Disco, welcher nicht mehr gebrauchte Gläser einsammelt.

    Mal ganz davon abgesehen kannst du natürlich auch mit dem Garbage Collector arbeiten, den musst du aber im Target aktivieren. Dann kannst du auch anObject=null; schreiben. :)
     
  9. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Blödsinn, das habe ich schon geschrieben, bevor ich überhaupt nur an ein Buch dachte. Schau mal in die Wiki des OS-X-Entwicklerforums. Information hilft ja bei dem Abbau von Vorurteilen. Übrigens verdiene ich nichts an dem Buch, wenn man die Oppertunitätskosten bedenkt. Insofern habe ich von Werbung nichts. Wenn ich Geld verdienen will, schreibe ich kein Buch.

    Inzwischen steht es auch in der Doku. Nur scheinen manche "alte Granden" einfach nur das Zeug herunterzuschreiben, weil sie das schon immer so gemacht haben.
     
    #9 Amin Negm-Awad, 22.09.09
    Zuletzt bearbeitet: 22.09.09
  10. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Weil du eine offene Ressource hinterlässt. Zuweilen vergisst man dann einfach die zu schließen, manchmal geht das auch gar nicht.
    Code:
    for( … ) 
       NSString* helper = [[NSString alloc] init];
       …
       if( … ) {
          break;
       }
       …
    }
    Entsprechendes gilt für Exceptionblöcke, in denen du dann alle zwischenzeitlich angehäuften Instanzen beseitigen darfst. Viel Spaß!

    Auch das Code-Beispiel ist ein Beispiel. Natürlich wäre die erneute Zuweisung völlig ungefährlich, wenn man den ARP verwendet. Dass dies dem Autor einen besonderen Hinweis wert ist, sagt alles. Stellt sich nur die Frage, warum er nicht gleich auf die Möglichkeit des ARP hinweist. Er kann ja notfalls auf später verweisen, wenn er es da noch nicht besprechen will.

    Außerdem erschwerst du die Erzeugung in besonderen Fällen wie Twintoning. Überlege dir mal eine Implementierung von -initWithString: (NSString). Dann wirst du selbst sehen, was du dir dabei einhandelst.

    Ja

    Hintergrund ist die Speicherknappheit. Grundsätzlich leben Objekte im ARP länger, das ist also potentiell verschwendend. Daher empfehlen auf dem iPhone noch einige, mit -release gleich wieder freizugeben. Ich halte die Verwendung eines eigenen ARP allerdings für besser, siehe oben.
     
    #10 Amin Negm-Awad, 22.09.09
    Zuletzt bearbeitet: 22.09.09
  11. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Du siehst das richtig, nur noch eine Bemerkung:

    Der Code verstößt auch noch gegen die Naming-Conventions. Eine solche "-add:"-Methode müsste etwas addieren, nicht aber eine neue Instanz erzeugen. Richtig müsste die -fractionByAddingFraction: oder ähnlich heißen. Dann müsste man auch nicht herumraten.

    Aber in Büchern verstehe ich den Hang zu kurzen Namen. Der Platz ist einfach extrem beschränkt. Nur: Wenn es gerade darauf ankommt, sollte man das richtig machen.
     
  12. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Geiles Gleichnis.
     
  13. Jamsven

    Jamsven London Pepping

    Dabei seit:
    21.11.07
    Beiträge:
    2.046
    Danke! :)
     
  14. Drobs

    Drobs Carola

    Dabei seit:
    23.05.08
    Beiträge:
    115
    Abgesehen davon, dass man es besser machen sollte, müsste man in dieser Version nicht noch sum2 freigeben? (Frage B)
     
  15. Jamsven

    Jamsven London Pepping

    Dabei seit:
    21.11.07
    Beiträge:
    2.046
    Nein, weil ja bei der letzten Iteration von der for Schleife sum=sum2 gesetzt wurde.
    Danach bricht die Schleife ab und sum wird released.
    sum=sum2 kopert nicht das Objekt, sondern dessen Speicheradresse.
    Dabei wird der retain counter auch nicht inkrementiert.

    Du kannst übrigends den retain count mit
    Code:
    [objektname retainCount]
    abfragen. Daran kannst du sehen wie viel mal released werden muss. Vergiss aber dann bei anderen Programmen den ARP nicht. Der hat dann auch counts, welche du nicht beachten musst.
     
    #15 Jamsven, 22.09.09
    Zuletzt bearbeitet: 22.09.09
  16. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Wenn man die Einhaltung der Naming-Conventions unterstellt: Nein.

    Grndsätzlich gilt, dass nur wenige Methoden eine offene Referenz liefern dürfen, nämlich
    +alloc…
    -copy…
    -mutableCopy…
    +new

    Bei allen anderen Methoden erhältst du keine Inhaberschaft an dem Rückgabewert. Daher darfst du ihn auch nicht freigeben.

    Diese Regel kann zu Problemen führen, wenn man sich folgende Situation vor Augen führt:
    Code:
    Person* person = … 
    NSString* oldName = person.name;
    person.name = @"Amin";
    Da an oldName keine Inhaberschaft besteht, ist das Ding jetzt verschwunden. Aus diesem Dilemma existieren verschiedene Wege:

    1. Rückgabe im ARP
    Code:
    - (NSString*)name {
       return [[name retain] autorelease];
    2. ARP im Sender
    Du kannst das auch in der versendenden Methode machen:
    Code:
    Person* person = … 
    NSString* oldName = [[person.name retain] autorelease];
    person.name = @"Amin";
    3. Kopieren
    Code:
    Person* person = … 
    NSString* oldName = [NSString stringWithString:person.name];
    person.name = @"Amin";
    4. Aufpasse
    :)
     
  17. Drobs

    Drobs Carola

    Dabei seit:
    23.05.08
    Beiträge:
    115
    Gut, danke für die Info.
     
  18. Tekl

    Tekl Fairs Vortrefflicher

    Dabei seit:
    01.06.05
    Beiträge:
    4.622
    Das mit der Buchwerbung war ja mehr ironisch gemeint. Mir ist schon klar, dass Schreiben meist kein Gewinngeschäft und mehr Liebhaberei ist, kenne es aus eigener Erfahrung.

    Danke auch für die Antwort auf meine Detailfrage. Mehr als das Prinzip habe ich aber nicht verstanden und ich muss sagen, dass mich die Syntax von ObjC ganz schön abschreckt. Bleibe wohl doch bei meinen Skriptsprachen, da kann ich’s so schreiben wie ich denke und muss nicht unzählige Dinge schreiben, an die ich nicht denke. Alleine das Beispiel mit dem oldName ist extrem gruselig. Kein Wunder, dass Software so voller Bugs ist, wenn so simple Dinge so kompliziert sind.

    Gibt’s eigentlich eine Möglichkeit mit einer anderen Skriptsprache als dem ebenso gruseligen AppleScript eine native und relative performante Cocoa-Anwendung zu entwickeln wo man letztendlich ein Kompilat ausliefert? Nein, RealBasic ist nicht performant, um das gleich mal vorweg zu nehmen.
     
  19. Amin Negm-Awad

    Amin Negm-Awad Süsser Pfaffenapfel

    Dabei seit:
    01.03.07
    Beiträge:
    665
    Na ja, es gibt einige Bridges. Aber Syntax ist doch Gewöhnung. Lasse dich davon nicht abschrecken, sondern versuche es mal ein paar Monate.
     
  20. Jamsven

    Jamsven London Pepping

    Dabei seit:
    21.11.07
    Beiträge:
    2.046
    Ja Ruby: http://www.macruby.org/
     

Diese Seite empfehlen