NSMutableArray ist immer (null) als dataSource für eine TableView

  • NSMutableArray ist immer (null) als dataSource für eine TableView

    Hi,

    ich möchte eine TableView dynamisch mit Einträgen füllen. Dazu habe ich eine DataSource mit einem NSMutableArray definiert. Aber irgendwie ist dieses NSMutableArray immer (null) wenn ich es mir zum Test mit NSLog ausgeben lasse. Irgendwas habe ich in meiner Klasse wohl vergessen - nur was?

    Hier mal mein Quellcode:

    Results.h
    ------------------------------------------------------------------------------------
    #import

    @interface Results : NSObject {
    IBOutlet NSTableView *tableView;
    NSMutableArray *paths;
    }

    @property (retain) NSMutableArray *paths;

    + (id)results;

    - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
    - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;

    - (void)addPath:(NSString *)path;

    @end


    Results.m
    ------------------------------------------------------------------------------------
    #import "Results.h"

    @implementation Results

    @synthesize paths;

    + (id)results {
    return [[[Results alloc] init] autorelease];
    }

    - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return [paths count];
    }

    - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    return @"foo";
    }

    - (void)addPath:(NSString *)path {
    [paths addObject:path];
    NSLog(@"%@", paths);
    [tableView noteNumberOfRowsChanged];
    [tableView reloadData];
    }

    @end


    Kann mir jemand sagen, wo das Problem liegt. Ich habe nun schon den ganzen Tag Bücher gewälzt und in der Dokumentation und im Internet gelesen, aber irgendwie finde ich den Fehler nicht. Bin schon ganz durcheinander im Kopf ... :(

    Dirk
  • +hm+ Seltsam, seltsam.
    Einen direkten Fehler sehe ich da jetzt nicht, aber ich finde, dass

    C-Quellcode

    1. @property (retain) NSMutableArray *paths

    eine ungünstige Lösung ist.

    Was immer von sonst wo (auch außerhalb) an deinem Array herumfuhrwerkt, wirft dir mitten drin alles durcheinander.

    Bei mir sähe es so aus:

    C-Quellcode

    1. @interface Results : NSObject {
    2. IBOutlet NSTableView *tableView;
    3. NSArray *paths;
    4. }
    5. @property (copy) NSArray *paths;
    6. ...
    7. @end


    Dementsprechend muss natürlich die Methode angepasst werden.

    C-Quellcode

    1. - (void)addPath:(NSString *)path {
    2. NSMutableArray* newPaths = [NSMutableArray arrayWithArray:[self paths]]; //Ich mag Getter.
    3. [newPaths addObject:path];
    4. NSLog(@"%@", newPaths);
    5. [self setPaths:newPaths]; //Setter mag ich auch.
    6. NSLog(@"%@", [self paths]);
    7. [tableView noteNumberOfRowsChanged];
    8. [tableView reloadData];
    9. }
    «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
  • Hi,

    macmoonshine schrieb:

    Deine path-Property ist natürlich immer nil, weil Du sie nirgendwo initialisierst.

    Okay, ist nun so drin:

    Quellcode

    1. - (id) init {
    2. self = [super init];
    3. if (self != nil) {
    4. self.paths = [NSMutableArray array];
    5. }
    6. return self;
    7. }

    Es war mir schon irgendwie bewusst, dass man von NSMutableArray noch eine Instanz anlegen muss, aber ich bin leider immer an der exakten Syntax gescheitert und habe nur Fehler bekommen. Nun ist es klar.

    macmoonshine schrieb:

    Ein dealloc mit Freigabe des Arrays wäre übrigens auch cool.

    Okay, ist nun so drin:

    Quellcode

    1. - (void) dealloc {
    2. [paths release];
    3. [super dealloc];
    4. }


    Dirk
  • Dirk Einecke schrieb:

    Okay, ist nun so drin:


    Quellcode

    Quellcode

    1. - (id) init {
    2. self = [super init];
    3. if (self != nil) {
    4. self.paths = [NSMutableArray array];
    5. }
    6. return self;
    7. }


    Es war mir schon irgendwie bewusst, dass man von NSMutableArray noch eine Instanz anlegen muss, aber ich bin leider immer an der exakten Syntax gescheitert und habe nur Fehler bekommen. Nun ist es klar.


    Und das erzeugt keinen Warning ?

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Hi,

    Thallius schrieb:

    Dirk Einecke schrieb:

    Okay, ist nun so drin:
    Quellcode

    Quellcode

    1. - (id) init {
    2. self = [super init];
    3. if (self != nil) {
    4. self.paths = [NSMutableArray array];
    5. }
    6. return self;
    7. }


    Es war mir schon irgendwie bewusst, dass man von NSMutableArray noch eine Instanz anlegen muss, aber ich bin leider immer an der exakten Syntax gescheitert und habe nur Fehler bekommen. Nun ist es klar.


    Und das erzeugt keinen Warning ?


    Nein. Warum?

    Dirk
  • Dirk Einecke schrieb:

    ... und ich müsste doch eigentlich in der "dealloc" Methode kein [paths release] machen ... weil mit self.paths = [NSMutableArray array] werde ich ja nicht Besitzer der Instanz und muss mich also nicht drum kümmern. Oder?

    Doch, Du verwendest ja eine retained Property, was auch richtig ist. Die sendet ein retain bei der Zuweisung.

    BTW:

    Quellcode

    1. self.paths = nil
    ist sauberer ;)
    „Meine Komplikation hatte eine Komplikation.“
  • Dirk Einecke schrieb:

    OK. Habe ich geändert. Aber wo genau ist der Unterschied ob ich es nun mit release oder nil mache?

    Es ist sauberer immer über die Accessoren auf die Werte zuzugreifen. Durch self.paths = nil; sendest Du nicht nur ein release an den Wert, sondern Du setzt das Attribut auch auf nil. Im dealloc ist das nicht so kriegsentscheidend. An anderen Stellen aber schon, z. B.:

    Quellcode

    1. [paths release];
    2. self.paths = [NSMutableArray array];
    geht in der Regel gründlich in die Hose.

    Generell wird die Speicherverwaltung viel einfacher, wenn Du nur über Property-Zugriffe arbeitest.
    „Meine Komplikation hatte eine Komplikation.“
  • Thallius schrieb:

    Weil in meiner Doku NSMutableArray keine Methode array kennt. Aber eventuell ist das bei iOs anders?

    Das stimmt nicht.

    Allerdings muss an dieser Stelle noch ein retain rein, dann array ist ein convenience-Allocator und liefert ein Objekt mit autorelease zurück.

    #EDIT: OK, dads was ich geschrieben habe, stimmt auch nicht. Sorry, Denkfehler. Ich les jetzt was Einführendes.
  • macmoonshine schrieb:

    Deine path-Property ist natürlich immer nil, weil Du sie nirgendwo initialisierst.

    Stimmt, da fehlte das -(id)init.
    Muss ich wegen der vorhandenen +(id)results wohl davon ausgegangen sein, dass sie es einfach nur nicht in das Posting geschafft hat.
    Fail meinerseits, sorry.

    macmoonshine schrieb:

    Doch, Du verwendest ja eine retained Property, was auch richtig ist.

    Ansichtssache, vor Allem bei veränderlichen Instanzvariablen.
    Schließlich bekommst du via Getter ja den Speicherbereich dieses Objektes und kannst das auch von außerhalb manipulieren.
    Das will man in der Regel bei Datenquellen nicht.
    Sonst nimmt man mal fürn Test zwei Objekte aus der Datenquelle und tauscht zwei andere aus, vergisst das TableView neu laden zu lassen und erinnert sich nicht mehr an den Test. Beim nächsten wie auch immer erzwungenen -reloadData versteht Mensch dann gar nichts mehr.
    (deshalb würde ich auch einen eigenen Setter gebaut haben, der nach Änderung des Arrays stumpf ein -reloadData sendet.)

    Wie dem auch sei, ich rate gern von der Benutzung manipulierbarer Objekte als Instanzvariablen mit einem retain-Setter ab.
    «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:

    macmoonshine schrieb:

    Doch, Du verwendest ja eine retained Property, was auch richtig ist.

    Ansichtssache, vor Allem bei veränderlichen Instanzvariablen.
    Schließlich bekommst du via Getter ja den Speicherbereich dieses Objektes und kannst das auch von außerhalb manipulieren.
    Das will man in der Regel bei Datenquellen nicht.
    Sonst nimmt man mal fürn Test zwei Objekte aus der Datenquelle und tauscht zwei andere aus, vergisst das TableView neu laden zu lassen und erinnert sich nicht mehr an den Test. Beim nächsten wie auch immer erzwungenen -reloadData versteht Mensch dann gar nichts mehr.
    (deshalb würde ich auch einen eigenen Setter gebaut haben, der nach Änderung des Arrays stumpf ein -reloadData sendet.)

    Wie dem auch sei, ich rate gern von der Benutzung manipulierbarer Objekte als Instanzvariablen mit einem retain-Setter ab.

    Sehe ich auch so. Mit richtig meinte ich den Zusammenhang. Er verwendet ein Mutable-Array und da würde copy nicht funktionieren. Das Fass mit NSArray vs. NSMutableArray hast Du ja schließlich schon aufgemacht. ;)
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Dirk Einecke schrieb:

    OK. Habe ich geändert. Aber wo genau ist der Unterschied ob ich es nun mit release oder nil mache?

    Es ist sauberer immer über die Accessoren auf die Werte zuzugreifen. Durch self.paths = nil; sendest Du nicht nur ein release an den Wert, sondern Du setzt das Attribut auch auf nil. Im dealloc ist das nicht so kriegsentscheidend.


    Ist aber auch im dealloc nützlich.
    Ich setze mittlerweile alle objekt-properties auf nil im dealloc. Egal ob retain, assign, copy.
    Durch die Setter schadet es ja nicht. Bei retain wird z.B. brav ein release geschickt an den bisherigen Wert, bei assign nicht.

    Ändert man in der Deklaration nun eine property von Retain nach Assign, kann ich mir sicher sein, dass die dealloc Methode immer noch sauber arbeitet ohne sie anfassen zu müssen.