NSTableView, datasource et données en colonnes

kangourou38kangourou38 Membre
05:45 modifié dans API AppKit #1
Voilà , je débute en cocoa, mais pas en programmation, et je cherche quelques éclaircissements sur le dataSource d'un NSTableView.
Pour différentes raisons, mes données doivent impérativement organisées en 4 colonnes (pour l'instant) d'exactement 128 objets (par exemples des NSNumber). J'ai pensé à  l'organisation suivante :
mes données sources sont un NSMutableDictionary dont chaque élément contiendra :
- un identifier NSString
- un NS(Mutable)Array qui contiendra les 128 pointeurs (pardon objets !) vers mes NSNumber.

Dans IB, j'ai sous-classé un NSMutableDictionary en MyDictionary, puis je l'ai instancié. J'ai crée un NStableView à  4 colonnes (dont j'ai nommé respectivement "col1",...,"col4" les identifiers de mes 4 NSTabColumn), puis j'ai connecté l'outlet dataSource de mon NSTableView vers mon instance.

j'ai ensuite implémenté les méthodes suivantes ci-dessous.

A l'execution, j'ai toujours un message d'erreur m'indiquant que -count ou -enumerator ne sont pas implémentés. Je ne sais pas pourquoi.

Sauriez-vous également de quelle façon le NSTableView communique avec son datasource ? est-ce uniquement avec les méthodes suivantes ??
- (void)tableView:setObjectValue:forTableColumn:row:
- (int)numberOfRowsinTableView:
- (id)tableView:objectValueForTableColumn:row:

Merci pour votre aide.

- (void)awakeFromNib
{
[ self addColumn : @col1 ] ;
[ self addColumn : @col2 ] ;
[ self addColumn : @col3 ] ;
[ self addColumn : @col4 ] ;
}

-(void)addColumn: (NSString*)nstr
{
NSNumber *number ;
NSMutableArray *array = [ NSMutableArray arrayWithCapacity: 128 ] ;
int i ;

for ( i = 0 ; i < 128 ; i++ )
{
  number = [ NSNumber numberWithDouble: 0.0 ] ;
  [ array addObject: number ] ;
// [ number release ] ; autorelease déjà  fait ?
}

[ self setObject: array forKey: nstr ] ;
[ array release ] ;
}

-(int)numberOfRowsinTableView: (NSTableView*)the_table
{
return 128 ;
}

-(id)tableView:(NSTableView *)the_table objectValueForTableColumn:(NSTableColumn *)the_col row:(int)num_row
{
NSString *identifier = [ the_col identifier ] ;
NSArray *array = [ self objectForKey: identifier ] ;
return [ array objectAtIndex : num_row ] ;
}

- (void)tableView:(NSTableView *)the_table setObjectValue:(id)the_obj forTableColumn:(NSTableColumn *)the_col row:(int)num_row
{
NSString *identifier = [ the_col identifier ] ;
NSMutableArray *array = [ self objectForKey: identifier ] ;
[ array replaceObjectAtIndex: num_row withObject: the_obj ] ;
}

Réponses

  • ChachaChacha Membre
    juin 2006 modifié #2
    Salut,

    Quelques réponses :

    dans 1151677616:

    mes données doivent [être] en 4 colonnes (...) d'exactement 128 objets (...)

    Ton approche avec MyDictionary me semble un datasource tout à  fait adapté.



    A l'execution, j'ai toujours un message d'erreur m'indiquant que -count ou -enumerator ne sont pas implémentés. Je ne sais pas pourquoi.

    C'est un premier signe que quelque chose ne se passe pas comme prévu : il y a une confusion d'objets, ce qui a généralement deux causes :
       -soit une de tes méthodes a renvoyé un mauvais objet (genre un NSNumber au lien d'un NSArray)
       -soit il y a une corruption de la mémoire due à  une erreur dans ta gestion mémoire (et il y en a au moins une dans ton code ci-dessous)


    Sauriez-vous également de quelle façon le NSTableView communique avec son datasource ?

    La doc de NSTableView et NSTableDataSource indique quelles sont les méthodes requises et optionnelles pour un datasource (ainsi que pour le delegate).


    - (void)tableView:setObjectValue:forTableColumn:row:
    - (int)numberOfRowsinTableView:
    - (id)tableView:objectValueForTableColumn:row:

    Ce sont effectivement les trois seules méthodes requises pour un datasource

    <br />-(void)addColumn: (NSString*)nstr <br />{<br />...<br /> // [ number release ] ; autorelease déjà  fait ?<br /> }<br />
    

    Si tu te poses la question, c'est qu'il te faut encore travailler un peu sur la gestion mémoire en Objective-C ! (Et oui, c'est dur à  saisir au début). En bref:
    Tu ne dois faire release que si tu as fait retain, copy, ou appelé un constructeur avec initxxx...
    Donc là , l'autorelease est déjà  fait.

    <br />-(void)addColumn: (NSString*)nstr <br />{<br />...<br /> NSMutableArray *array = [ NSMutableArray arrayWithCapacity: 128 ] ;<br />...<br /> [ array release ] ;<br />}<br />
    

    Il y a donc une erreur ici : le array est déjà  autoreleasé ! Il ne faut pas faire de release supplémentaire (ou alors, il faut changer [NSMutableArray arrayWithCapacity: 128 ] en [[NSMutableArray alloc] initWithCapacity:128]).

    Cela résout-il ton problème ?

    +
    Chacha

    [edit]
    Je viens de voir, ce n'est pas
    numberOfRowsinTableView
    mais
    numberOfRowsInTableView
    [/edit]

    [edit2]
    Oh, je n'avais pas vu, c'est ton premier post : Bienvenue sur Objective-Cocoa !
    [/edit2]
  • BruBru Membre
    05:45 modifié #3
    dans 1151677616:

    Dans IB, j'ai sous-classé un NSMutableDictionary en MyDictionary, puis je l'ai instancié. J'ai crée un NStableView à  4 colonnes (dont j'ai nommé respectivement "col1",...,"col4" les identifiers de mes 4 NSTabColumn), puis j'ai connecté l'outlet dataSource de mon NSTableView vers mon instance.
    [...]
    A l'execution, j'ai toujours un message d'erreur m'indiquant que -count ou -enumerator ne sont pas implémentés. Je ne sais pas pourquoi.


    dans 1151679700:

    dans 1151677616:

    mes données doivent [être] en 4 colonnes (...) d'exactement 128 objets (...)

    Ton approche avec MyDictionary me semble un datasource tout à  fait adapté.


    Apple déconseille fortement de sous-classer NSDictionary/NSMutableDictionary (ce sont des class-cluster).
    Mais, si dans un accès de folie incontrôlée, tu décides de le faire, alors il faut ABSOLUMENT surcharger les méthodes count, objectForKey: et keyEnumerator. Visiblement, c'est ce qui fait planter ton appli.

    Dans ton cas, je ne vois pas l'intérêt de faire ça.
    Un bon vieux objet contrôleur (dérivant de NSObject) dans lequel tu implémentes les 3 méthodes datasource et dans lequel tu stockes dictionnaires et tableau pour le contenu de la table est suffisant.

    .
  • kangourou38kangourou38 Membre
    05:45 modifié #4
    En fait, j'avais commencé en instanciant une sous-classe de NSObject, mais j'avais un i minuscule à  la place d'un I majuscule (voir plus haut), et comme je ne trouvais pas mon erreur, j'avais essayé une variante en sous-classant NSMutableArray, ce qui semble délicat (qu'est-ce qu'on peut faire des bétises par moment !)
    Merci pour votre aide
  • kangourou38kangourou38 Membre
    05:45 modifié #5
    En fait, la gestion de la mémoire sous cocoa ne me semble pas très complexe. Là , je sens que je vais en faire hurler certains ! Il suffit de savoir quelles sont les méthodes qui font des autorelease.
    Le système des retain et release est astucieux (j'avais d'alleurs implémenté un système semblable pour mon appli - GEMGraph pour les connaisseurs - lorsque je programmais sous GEM sur atari st). Une seule question qui me hante à  propos des autorelease : QUAND ont-ils lieu ? Pas trop tôt, sinon le pointeur ne sert à  rien, mais je l'espère pas trop tard car sinon, plein de mémoire gaspillée.
    En fait, ce que je ne comprend pas, c'est pourquoi l'objective-C n'autorise pas à  mettre des objets (ou leur pointeur) dans une pile plutôt que de faire systématiquement des allocations mémoire. Les autorelease ne seraient plus nécessaire !
  • AliGatorAliGator Membre, Modérateur
    05:45 modifié #6
    Ben l'autoreleasepool c'est justement cette fameuse "pile de pointeurs" dont tu parles :)

    Il y a certains intérêts à  gérer la mémoire soi-même, par opposition par exemple au Garbage Collector de Java, qui est qu'on peut relâcher les objets dès qu'on sait qu'on n'en a plus besoin, et donc d'être plus efficace... et aussi moins lourd à  l'execution.

    Une autorelease pool est créée par Cocoa au début de chaque RunLoop et relâchée à  chaque fin de RunLoop. Donc les objets qui sont "autoreleasés" sont relâchés à  la fin de chaque RunLoop, ce qui répond à  ta question :)

    Il est aussi possible de créer et relâcher tes propres autoreleasepool. Ceci est utile justement quand tu as beaucoup d'allocations et d'objets autoreleasés à  un moment dans ton code, en particulier sur une grosse boucle for ou while qui alloue de grandes quantités de donnnées (il suffit simplement de penser à  une boucle qui alloue 3 ou 4 images autoreleasées, ça prend vite de la mémoire)
    Donc dans ce cas de figure là  on alloue une AutoreleasePool au début (et à  l'intérieur) de la boucle et on le relâche à  la fin. En le relachant, il se détruit, et détruit surtout tous les objets à  qui on a envoyé un autorelease depuis sa création.

    Donc en général c'est pas relâché trop tôt, et si tu as peur que ce soit relâché trop tard et gaspiller de la mémoire, tu peux créer des pools intermédiaires.
    Quant à  l'allocation totalement dynamique au lieu de dans une pile, c'est l'originalité de l'oBjective-C d'être totalement dynamique. Ca a des inconvénients, mais aussi pas mal d'avantages, donc bon.

    Et pour savoir quelles sont les méthodes qui renvoient des objets autorelease et celles pour qui c'est à  toi de gérer la mémoire, ben c'est pas trop dur à  savoir : si c'est toi qui alloue explicitement la mémoire avec un alloc ou un copy c'est à  toi de la relâcher avec un release. Et si tu fais des retain, il faut aussi les balancer avec des release.
    Les constructeurs de commodité, qui ont leur nom commençant par le nom de la classe ([NSNumber numberWith...], [NSDictionary dictionaryWith...], [NSString stringWith...] etc), sont là  exprès pour créer des objets temporaires, donc autoreleasés, et sont exactement équivalents à  un alloc+init+autorelease, donc pour eux tu n'as pas à  faire de release. Et pour tout les autres trucs, genre les objets retournés par des fonctions, ils sont autoreleasés, par convention, pour continuer à  suivre la logique.
  • ChachaChacha Membre
    05:45 modifié #7
    dans 1151679700:

    dans 1151677616:

    mes données doivent [être] en 4 colonnes (...) d'exactement 128 objets (...)

    Ton approche avec MyDictionary me semble un datasource tout à  fait adapté.



    Apple déconseille fortement de sous-classer NSDictionary/NSMutableDictionary (ce sont des class-cluster).


    Bru a raison, j'ai dit une bêtise. En fait, j'avais lu de travers, je croyais que tu utilisais un dictionary contenant 4 array, mais je n'avais pas fait attention à  l'héritage... mea culpa !

    +
    Chacha
Connectez-vous ou Inscrivez-vous pour répondre.