Property Zuweisung, nur eine Stilfrage?

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

  • Property Zuweisung, nur eine Stilfrage?

    Immer wieder komme ich ins wanken, bei Zuweisungen von neu erstellten Objekten.

    Ich habe die folgende Klasse mit einem Mutable Array

    Quellcode

    1. @interface test : NSObject {
    2. NSMutableArray *myArray;
    3. }
    4. @property (nonatomic, retain) NSMutableArray *myArray;
    5. @end


    Der folgende Code, hilft zwar sehr schön die Zuweisung und den auch den "Owner" zu erkennen, auch die release-Frage ist klar zu erkennen. Aber eben 3 Zeilen Code nur dafür dass "mir" dieses Array gehört.

    Quellcode

    1. NSMutableArray *array = [[NSMutableArray alloc] init];
    2. self.myArray = array; // Im Setter ist Release drin
    3. [array release];


    Der folgende Code ist kurz und praktisch. Für's Hirn aber am anfang nicht so einfach nachzuvollziehen. Und einige finden das autorelease nicht ganz effektiv.

    Quellcode

    1. self.myArray = [[[NSMutableArray alloc] init] autorelease];


    Der folgende Code würde ich z.B. in der init-Methode ok finden, da myArray da ja sicher noch nichts zum 'retainen' hat.

    Quellcode

    1. myArray = [[NSMutableArray alloc] init];


    Bei der dealloc-Methode bin ich auch nicht sicher, was besser ist.

    Quellcode

    1. - (void)dealloc
    2. {
    3. [myArray release];
    4. myArray = nil;
    5. [super dealloc];
    6. }


    oder besser

    Quellcode

    1. - (void)dealloc
    2. {
    3. self.myArray = nil;
    4. [super dealloc];
    5. }


    Habt ihr für euch Grundregeln, wie ihr vorgeht?
    Gruss, und Dank und Entschuldigung für wahrscheinlich immer wieder die gleiche Fragestellung zu diesem Thema.
  • So, jetzt mal was zur Verwirrung.

    Die Modern Runtime erlaubt ja, das ich die Angabe einer iVar für ein Property ganz weg lasse (sehr praktisch, wie ich finde).

    Quellcode

    1. @interface OCClass : NSObject
    2. {
    3. }
    4. @property (assign, readwrite) NSRect frame;
    5. @property (retain, readwrite) NSMutableArray *anArray;
    6. @end


    jetzt wirds gut. Ich kann jetzt innerhalb der Implementation anArray direkt ansperechen, also anArray = ... Wird jetzt der Setter von anArray benutzt oder ist das nur eine Zuweisung an die synthetisierte iVar - der Compiler ist sich da wohl selbst nicht so sicher.

    Versucht mal folgendes:

    Quellcode

    1. @implementation OCClass
    2. @synthesize frame, anArray;
    3. - (id)init
    4. {
    5. self = [super init];
    6. if (self) {
    7. anArray = [[NSMutableArray alloc] init];
    8. //self.anArray = [[NSMutableArray alloc] init];
    9. //NSLog(@"self array %@", [self anArray]);
    10. NSLog(@"retain count %i", (int)[anArray retainCount]);
    11. [self setAnArray: anArray];
    12. NSLog(@"retain count %i", (int)[anArray retainCount]);
    13. [anArray retain];
    14. NSLog(@"retain count %i", (int)[anArray retainCount]);
    15. NSMutableArray *a = [[NSMutableArray alloc] init];
    16. [self setAnArray: a];
    17. NSLog(@"retain count a %i", (int)[anArray retainCount]);
    18. NSLog(@"retain count anArray %i", (int)[anArray retainCount]);
    19. [a release];
    20. [anArray release];
    21. }
    22. return self;
    23. }
    24. - (void)dealloc
    25. {
    26. [anArray release];
    27. [super dealloc];
    28. }
    29. @end
    Alles anzeigen


    anArray = [[NSMutableArray alloc] init]; Der Compiler scheint eine Zuweisung zu einer Instanzvariable? anArray zu machen. Jedenfalls wird anArray nicht retained.

    Wenn ich jetzt [self anArray] aber mal logge, dann stimmt der RC, in dem Fall zwei - einfach durch einen Lesezugriff in der nächsten Zeile, scheints jetzt richtig zu funktionieren. Das Array geht durch den Setter. Schon ein etwas komisches Verhalten.

    Das NSRect ist eigentlich nur für meine übliche FastAccess Kritik. Anfänger aufgepasst, hier kommt Beispielcode

    Quellcode

    1. self.frame.width.height = 280;
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • Manfred Kreß schrieb:

    C-Quellcode

    1. self.frame.width.height = 280;


    frame.width.height???

    Sicher, dass du nicht das meinst:

    C-Quellcode

    1. self.frame.size.height = 280;
    «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
  • Generell bin auch ich für den Einzeiler mit Convenience Allocater. Dieser steht im -init und im -dealloc gibts ein einfaches [self setArray:nil];
    Speziell hier bin ich für einen Copy-Setter anstelle eines Retain-Setters. Du willst keine Referenz auf ein sich veränderndes Array.
    «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
  • Lucas de Vil schrieb:

    peziell hier bin ich für einen Copy-Setter anstelle eines Retain-Setters. Du willst keine Referenz auf ein sich veränderndes Array.


    Speziell hier bist du dann mit dem Problem konfrontiert, das copy auch copy benutzt und nicht mutableCopy ;)

    Quellcode

    1. NSMutableArray *a = [[NSMutableArray alloc] init];
    2. [self setAnArray: a];
    3. [[self anArray] addObject: @"bum"];
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • Manfred Kreß schrieb:

    Lucas de Vil schrieb:

    peziell hier bin ich für einen Copy-Setter anstelle eines Retain-Setters. Du willst keine Referenz auf ein sich veränderndes Array.


    Speziell hier bist du dann mit dem Problem konfrontiert, das copy auch copy benutzt und nicht mutableCopy ;)

    Quellcode

    1. NSMutableArray *a = [[NSMutableArray alloc] init];
    2. [self setAnArray: a];
    3. [[self anArray] addObject: @"bum"];

    Jein.
    Dieses Problem tritt bei mir nicht auf, da ich _immer_ immutable Instanzvariablen nutze.
    Die halten dann als Referenz für die veränderlichen Varianten her, von denen dann stumpf die unveränderlichen Kopien gesichert werden.

    Einen Sinn in dem Nutzen mutabler Instanzvariablen sehe ich persönlich nicht.
    «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
  • Manfred Kreß schrieb:

    Wenn ich jetzt [self anArray] aber mal logge, dann stimmt der RC, in dem Fall zwei - einfach durch einen Lesezugriff in der nächsten Zeile, scheints jetzt richtig zu funktionieren. Das Array geht durch den Setter. Schon ein etwas komisches Verhalten.

    Nein, dass Array geht nicht durch den Setter und da ist auch nichts komisch dran, denn Du hast die Property mit dem Default atomic deklariert. Das bedeutet, dass der Getter vor der Rückgabe noch mal ein retain und autorelease dem zurückgegebenen Objekt zukommen lässt. Der Getter erhöht in diesem Fall den retain count, nicht der Setter.

    Michael
  • Du meinst etwa so:

    Quellcode

    1. - (void) addChild: (id)value
    2. {
    3. NSArray *theChilds = [self childs];
    4. NSMutableArray *tmp = [NSMutableArray arrayWithArray: theChilds]; // kopiert alle gespeicheten Pointer ins MA
    5. [tmp addObject: value];
    6. [self setChilds: tmp]; // macht über den setter nochmal ne copy eines Array und kopiert wieder alle 10000 Pointer rüber
    7. }
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • Michael schrieb:

    Nein, dass Array geht nicht durch den Setter und da ist auch nichts komisch dran, denn Du hast die Property mit dem Default atomic deklariert. Das bedeutet, dass der Getter vor der Rückgabe noch mal ein retain und autorelease dem zurückgegebenen Objekt zukommen lässt. Der Getter erhöht in diesem Fall den retain count, nicht der Setter.


    Vielen Dank! Daran hab ich wirklich überhaupt nicht gedacht.
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • Manfred Kreß schrieb:

    Du meinst etwa so:

    Quellcode

    1. - (void) addChild: (id)value
    2. {
    3. NSArray *theChilds = [self childs];
    4. NSMutableArray *tmp = [NSMutableArray arrayWithArray: theChilds]; // kopiert alle gespeicheten Pointer ins MA
    5. [tmp addObject: value];
    6. [self setChilds: tmp]; // macht über den setter nochmal ne copy eines Array und kopiert wieder alle 10000 Pointer rüber
    7. }

    Ode rnoch einfacher:

    Quellcode

    1. - (void) addChild: (id)value
    2. {
    3. [[self mutableArrayValueForKey:@"childs"] addObject:value];
    4. }
    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"?
  • Vielen dank an alle. Dies hat mir sehr geholfen.
    Auch wenn die Zusatz-Problemstellung von Manfred Kreß bei mir selber fast zu einem Release zu viel geführt hat.

    Generell kann ich noch sagen, dass bei einem schon einigermassen komplexen Projekt, bei welchem schon genug Hirnarbeit anfällt es sträflich ist, beim Memory-Management zu ungenau zu sein.
    Vor allem, wenn dann Objekte "sehr weit gereicht" werden, und entweder (so wie bei mir) dann zu einer Memory-Leak-Lawine führen, oder dann beim release eines weit gereichtem Objekt, zum crash führt, nur weil ganz am Anfang ein retain zu wenig war.
    Gerade da bin ich mir noch nicht sicher, ob es nicht manchmal besser wäre einfach eine Objekt-Kopie zuzulassen, damit ich oder das System keinen Stress bekommt.
    Bei meinem Projekt mache ich z.B. folgendes.
    Anfrage an Webserver.
    Dieser sendet XML-Daten.
    Konvertierung in ein Dictionary, welcher dann auch wieder Arrays und Dictionarys enthält.
    Diese Unterelemente werden weit gereicht, durch Views mit unterviews bis dann Teile davon z.B. in drawRect auch über CFStringRef angesprochen werden. Da führt dann jedes CFRelease zu Schweissperlen, und im Prinzip könnte dies ja ruhig noch eine Referenz auf das beim XML Konverter erzeugte Objekt sein, wenn man dann eben alle Retain, Release und CFRelease richtig einsetzt......

    Gruss und Dank, Oliver
  • Wenn Du die Memory-Management-Regeln einmal verinnerlicht hast und Dich immer daran hältst, musst Du Dir darüber im Grunde keinerlei Gedanken machen, ganz egal, wie gross das Projekt wird.
    Hier ein paar Regeln:

    - Zugriff auf ivars IMMER mittels gettern/settern (auch in der Implementation, ebenfalls in -init.. und -dealloc)
    - schreibe Dir convenience-allocators für alle eigenen Klassen
    - benutze die mitgelieferten CAs der Frameworks, falls es diese nicht gibt:

    Quellcode

    1. id instance = [ [ [ theClass alloc ] init... ] autorelease ];


    Gruß, Markus
  • Amin Negm-Awad schrieb:


    Oder noch einfacher:

    Quellcode

    1. - (void) addChild: (id)value
    2. {
    3. [[self mutableArrayValueForKey:@"childs"] addObject:value];
    4. }

    Das ist ja mal ganz groß. :)
    «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