NSMutableArray von Objekten (Speicherverwaltung)

  • NSMutableArray von Objekten (Speicherverwaltung)

    Hallo,

    ich bin total neu in der Objective-c Entwicklung. Habe mich schon aber schon zum Speicherverwaltungskonzept belesen(alloc,release, retain,...) Programmiere seit Jahren aber in Pascal,C und so weiter.

    Ich habe folgenden Programmcode geschrieben, welcher Objekte einer Klasse (test) in einem Array verwalten soll, und möcht einfach von euch mal wissen ob so korrekt verwendbar oder memleak noch entstehen könnte:

    test.h:

    Quellcode

    1. #import <Foundation/Foundation.h>
    2. @interface test: NSObject {
    3. NSInteger hint;
    4. NSString* Remark;
    5. }
    6. @property (nonatomic, assign) NSInteger hint;
    7. @property (nonatomic, retain) NSInteger type;
    8. @end;
    Alles anzeigen


    test.m

    Quellcode

    1. #import "test.h"
    2. @implementation test
    3. @synthesize hint;
    4. @synthesize Remark;
    5. @end


    Quellcode

    1. InputTestModel.h:
    2. #import <Foundation/Foundation.h>
    3. #import "test.h"
    4. @interface InputTestModel : NSObject {
    5. NSMutableArray* dataArray;
    6. }
    7. @property (nonatomic, retain) NSMutableArray *array;
    8. - (InputTestModel*) init;
    9. - (void) dealloc;
    10. - (NSInteger)createEntry:(test*)data;
    11. - (test*)getEntryAtIndex:(NSInteger)index;
    12. - (NSInteger)count;
    13. @end
    Alles anzeigen


    InputTestModel.m:

    Quellcode

    1. #import "InputTestModel.h"
    2. @implementation InputTestModel
    3. @synthesize array;
    4. - (InputTestModel*) init {
    5. self = [super init];
    6. if (self) array = [[NSMutableArray alloc] init];
    7. return self;
    8. }
    9. - (void)dealloc
    10. {
    11. [array removeAllObjects];
    12. [array release];
    13. array = nil;
    14. [super dealloc];
    15. }
    16. - (NSInteger)createEntry:(test*)data {
    17. NSInteger i;
    18. [array addObject:data];
    19. i = [array count]-1;
    20. return i;
    21. }
    22. - (test*)getEntryAtIndex:(NSInteger)index {
    23. return (test*)[array objectAtIndex:index];
    24. }
    25. - (NSInteger)count {
    26. return [array count];
    27. }
    28. @end
    Alles anzeigen



    und verwendet z.B. so in einer anderen Klasse durch InputTestModel* model; :

    Quellcode

    1. // Erzeugung in der init einer Klasse:
    2. - (void) init {
    3. [super init];
    4. model = [[InputTestModel alloc] init];
    5. }
    6. ..
    7. ..
    8. // Erzeugung in der init einer Klasse:
    9. - (void) irgendeineFunktion{
    10. test* data = [[test alloc] init];
    11. data.hint = 4;
    12. data.remark = @"hallo";
    13. NSInteger x = [model createEntry:data];
    14. test* data2 = [model getEntryAtIndex:x];
    15. NSLog(@"%d",data2.hint);
    16. [data release];
    17. NSLog(@"%d",data2.hint);
    18. }
    19. ..
    20. // Freigabe in der selben Klasse
    21. - (void)dealloc
    22. {
    23. ...
    24. [model release];
    25. model = nil;
    26. [super dealloc];
    27. }
    Alles anzeigen


    Funktionieren tut das jetzt alles erstmal so, aber bin mir nicht 100% ob alles korrekt freigegeben.
    Für jeden Hinweis auf Fehler oder Verbesserung, vielen Dank.


    MfG rio
  • Hallo Rio,

    ich weiß nicht nach welchem Buch Du lernst, aber das ist alles nicht so der Hit :)

    Wenn Du schon eine Property synthetisierst, dann solltest du bei der Zuweisung auch den Setter verwenden. Genauso solltest du dann statt einem release ein self.MyVar=nil benutzen. Dieses alloc/init Release händisch zu machen ist fürchterlich oldscool und führt zu vielen Fehlerquellen.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Nein das meine ich nicht. Du solltest vor allem Setter beim Initialisieren von Objekten benutzen. Und vor allem dann auch entsprechende Convenience Methoden der Klassen.

    Z.B. für Dein Array

    Quellcode

    1. @property (retain) NSMutableArry *array; // deklarieren
    2. @synthesize array; // synthetisieren erzeugt die Setter und Getter für das Array
    3. self.array=[NSMutableArray array] // Convenience instanzieren oder
    4. self.array=[[[NSMutableArray alloc] init] autorelease]; // instanzieren wenn es keine conveniance Methode gibt
    5. self.array=nil; // freigeben inclusive allem Inhalt vorher abräumen


    mehr brauchst du nicht. Der Vorteil ist, dass Du beliebig oft

    Quellcode

    1. self.array=[NSMutableArray array] // instanzieren
    2. self.array=[NSMutableArray array] // instanzieren
    3. self.array=[NSMutableArray array] // instanzieren
    4. self.array=nil;
    5. self.array=nil;
    6. self.array=nil;


    hintereinander schreiben kannst ohne einen Leak zu erzeugen oder einen Crash weil du etwas freigibst das schon freigegeben wurde.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • ok, danke erstmal für den hinweis. Bedeutet das, dass ich folgendes machen muss? Wäre der Code nun sauberer?

    InputTestModel.m

    Quellcode

    1. #import "InputTestModel.h"
    2. @implementation InputTestModel
    3. @synthesize array;
    4. - (InputTestModel*) init {
    5. self = [super init];
    6. if (self) self.array = [NSMutableArray array];
    7. return self;
    8. }
    9. - (void)dealloc
    10. {
    11. [self.array removeAllObjects];
    12. self.array = nil;
    13. [super dealloc];
    14. }
    15. - (NSInteger)createEntry:(test*)data {
    16. NSInteger i;
    17. [self.array addObject:data];
    18. i = [self.array count]-1;
    19. return i;
    20. }
    21. - (test*)getEntryAtIndex:(NSInteger)index {
    22. return (test*)[self.array objectAtIndex:index];
    23. }
    24. - (NSInteger)count {
    25. return [self.array count];
    26. }
    27. @end
    Alles anzeigen


    Bedeutet das, dass die Klasse automatisch sich im Speicher um das Array kümmern soll (autorelease)? Und was ist der unterschied das "Self" davor zuschreiben zu meinem Code wo das Self vernachlässigt wurde? Es gibt ja nur das "eine" Array in der Klasse. Heisst das auch, dass die Deklaration von NSMutableArray* dataArray in dem InputTestModel Interface dann wegfällt?

    Kannst du mir noch ein gutes Tutorial, Buch empfehlen wo die Objective-c Grundlagen wirklich reichlich gut beschrieben werden?

    Vielen Dank, MfG Rio

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Rio ()

  • Hallo Rio,

    wenn Du das self. davor setzt dann wird automatisch der Setter benutzt. Es ist genau das gleiche also

    Quellcode

    1. self.array=[NSMutableArray arry];
    2. [self setArray:[NSMutableArray array]];


    sind komplett identisch nur ist das erstes einfacher zu tippen. Das ist am Anfang etwas verwirrend vor allem wenn man von C kommt, wo der "." ja nur ein Strukturbezug ist.

    Das removeAllObjects kannst du Dir übrigens auch sparen, da der dealloc vom NSMUtableArray diesen automatisch selber macht.

    Das self auch bei abfragen zu schreiben: also z.B. deine

    Quellcode

    1. return [self.array count];


    ist ebenfalls sauberer bringt aber keinen nennenswerten Vorteil, so dass es von vielen aus Tippfaulheit weggelassen wird. Wirklich wichtig ist es nur bei Zuweisungen an eine iVar.

    Das autorelease macht nichts anderes als das mit alloc/init instanzierte Object beim der Rückkehr aus der Methode freizugeben (damt Amin jetzt nicht meckert füge ich hinzu das das stark vereinfacht ist und wirklichkeit natürlich viel komplexer). Wenn du nur

    Quellcode

    1. self.array={{NSmutableArray alloc] init]


    machen würdet, dann hättest du ein Leak, denn der alloc/init erzeugt einen owner (so hast du es ja vorher gemacht) und der Setter vom array (da es eine retain property ist) ebenfalls nochmal. Damit hast du 2 Owner auf dem Object obwohl du nur 1 haben willst, nämlich den von deinem array.

    Beim guten Buch streiten sich die Geister.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Bitte schlag mich nicht gleich, aber kann man das so verstehen, dass mit

    Quellcode

    1. self.ownarray=[NSMutableArray array];


    das "hauseigene" Array verwendet wird (also das Array welches in der Basisklasse automatisch erzeugt wird) statt ein eigenes ownarray meiner Klasse zu initialisieren (bzw. allozieren)?
    Und geh ich dann richtig der Annahme, dass darum ein einfaches self.ownarray= nil in der viewDidUnload-Methode reicht (statt noch explizit ownarray in dealloc zu releasen? D.h. die Deklaration von ownarray als eigenen instanz kann ich mir dann sowieso sparen, da ich ja nur ein property deklariere sollte, oder?

    Nur wenn ich eigene instanz erzeuge (mit retain) muss ich diese 100% selbstständig wieder freigeben, richtig?


    Wie sieht es aus, wenn ich ein array so deklarier:

    Quellcode

    1. NSArray * names; // keine property


    dann es so fülle:

    Quellcode

    1. names = [[NSArray alloc] initWithObjects:
    2. @"1",
    3. @"2",
    4. nil];


    dann muss ich explizit in der dealloc methode [names release]; schreiben oder?
    muss ich dafür dann überhaupt ein property extra deklarieren und wenn wie würde das sauber aussehen?

    Entschuldigung für all die verwirrenden Fragen, ich versuch nur gerade das ganze nachvollziehen zu können und vielen Dank, wenn ihr mir als Einsteiger etwas mehr durchsicht geben könnt..
    MfG rio
  • Ganz ehrlich. Du programmiert doch nicht wirklich seit Jahren ??? und Du hast Dich auch nicht wirklich in Speicherverwaltung eingelesen oder ?

    Du brauchst dringend ein Buch. So wird das nichts.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Rio schrieb:

    Fakt ist doch das alles was alloziert wird, auch wieder freigegeben werden muss.

    Richtig, aber. ;)

    Fakt ist, dass alles von dir freigegeben werden muss, auf das du Besitz angemeldet hast.

    Besitz meldest du immer mit -init*, -*copy* und -retain an.
    Besitz meldest du immer mit -*release ab.

    Beispiel mit deinem Array:

    C-Quellcode

    1. @interface
    2. {
    3. NSArray * ownArray;
    4. }
    5. - (void)setArray:(NSArray*)theArray;
    6. - (NSArray*)array;
    7. @end
    8. @implementation
    9. #pragma mark Init
    10. - (id)init
    11. {
    12. self = [super init];
    13. // Initialisiere Klasse mit einem leeren Array.
    14. if(self) ownArray = [[NSArray alloc] init];
    15. return self;
    16. }
    17. #pragma mark Setter/Getter
    18. /* Obacht: ungefähr äquivalent zu @property (copy) NSArray * array; @synthesize array = ownArray; */
    19. - (void)setArray:(NSArray*)newArray
    20. {
    21. // Setze das neue Array nur, wenn es mit dem aktuellen Array nicht identisch ist.
    22. if(ownArray != newArray)
    23. {
    24. // Gib den Besitzanspruch an das alte Array auf.
    25. [ownArray release];
    26. // Sollte das Array ins Nirvana zeigen, setze ein neues Array.
    27. if(newArray != nil)
    28. {
    29. // Erzeuge ein neues Array und melde Besitzanspruch an.
    30. ownArray = [[NSArray alloc] init];
    31. }
    32. else
    33. {
    34. // Melde Besitzanspruch an eine Kopie des Parameters an.
    35. ownArray = [newArray copy];
    36. }
    37. }
    38. }
    39. - (NSArray*)array
    40. {
    41. return ownArray;
    42. }
    43. #pragma mark Dealloc
    44. - (void)dealloc
    45. {
    46. [ownArray release];
    47. [super dealloc];
    48. }
    49. @end
    Alles anzeigen


    Mach mal ein Gedankenexperiment: was passiert bei folgendem Code?

    C-Quellcode

    1. NSArray * tempArray = [NSArray arrayWithObjects:@"Abre",@"Kadabre,",@"Ich wünsch' mir", @"de Baabe!",nil];
    2. myObject.array = tempArray;


    Du hast kein -init*, -*copy* oder -retain geschickt, also gibst du nichts frei!
    (Damals wurden diese Convenience Allocators als Äquivalent zu [[[NSArray alloc] initWithObjects:...] autorelease]; angesehen - das muss aber nicht so sein.)

    Du hast keinen Zugriff auf das Objekt, dein Setter zieht sich eine Kopie und speichert diese als Instanzvariable.


    C-Quellcode

    1. NSArray * tempArray = [[NSArray alloc] initWithObjects:@"Eens Zwo Eens Zwo",@"Volle Wolle,",@"Ick will zurück", @"uff meene Scholle!",nil];
    2. myObject.array = tempArray;
    3. [tempArray release];


    Oh, ein -init*! Du hast Besitz angemeldet, also mach diesen Besitz wieder weg. Sobald du das Ding nicht mehr brauchst! Versuche niemals, mittels zusätzlich gemauertem -retain dein Objekt länger zu halten als notwendig (a.k.a. über die Methode hinaus.)
    Sinnvoller, da man das -release immer(!) vergisst:

    C-Quellcode

    1. NSArray * tempArray = [[[NSArray alloc] initWithObjects:@"Eens Zwo Eens Zwo",@"Volle Wolle,",@"Ick will zurück", @"uff meene Scholle!",nil] autorelease];
    2. myObject.array = tempArray;


    Aber was passiert jetzt bei folgender Zeile?

    C-Quellcode

    1. myObject.array = nil;


    Bei dieser Setterkonstruktion musst du das Array selbst freigeben.
    Der Setter erstellt nämlich frech ein neues, leeres Array anstelle einfach eine Kopie von nichts zu setzen.
    (Wenn deine Klasse irgendwo mal [array objectAtIndex:0] weiterverarbeiten soll und kein Array findet, wird's lustig.)
    «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

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Lucas de Vil ()