Désallouer les cellules d'une NSMatrix

16:32 modifié dans API AppKit #1
Ben voila j'ai une NSMatrix, composée de deux colonnes et d'un nombre de lignes qui varie.

Suivant une sélection dans un NSTableView à coté, le contenu et la taille de la matrice va changer. J'affiche sans problème tout ce que je veux dans la matrice mais je ne désalloue jamais mes NSCell avec pour conséquence une fuite mémoire conséquente.

Comment je peux faire pour désallouer mes cellules avant d'en créer d'autres et sans tout faire planter (et sans avoir à détruire la matrice pour en recréer une autre, ce qui restet la solution qui doit marcher le mieux mais qui me demande de prendre le temps de comprendre comment initier programmatiquement un NSMatrix).

Pour info voici le code que j'utilise pour créer chaque ligne :

if([defaults boolForKey: @displayRevenus])
{
/*
* Revenus
*/
[matriceSysteme addRow];
[matriceSysteme putCell: [[NSCell alloc] initTextCell: @Revenus :]
atRow: i
column: 0];
[matriceSysteme putCell: NSCell alloc] initTextCell: <br /> [[_systemes objectAtIndex: [mySystemesNSTableView selectedRow revenus2]]
atRow: i
column: 1];
i++;
}

Réponses

  • nucleusnucleus Membre
    16:32 modifié #2
    Comment ta NSMatrix est-elle crée?
    En principe, c'est la NSMatrix qui gère la libération des cells qu'on lui a affecté..
  • 16:32 modifié #3
    Elle est construite dans IB et donc allouée automatiquement sans que j'ai à m'occuper.
    Ensuite vu que l'on ne peut pas faire un addRow: sur une matrice vide je fais un renewRows:columns:
    [matriceSysteme renewRows: 1 columns: 2];

    Lorsque la matrice disparait elle s'occupe de désalouer ses cellules, là dessus je suis ok mais moi ma NSMatrix je la garde et je renouvelle juste la totalité des cellules.
    En fait je reformule ma question : comment retirer des cellules d'une matrice ?

    Voici la solution que j'ai adoptée temporairement :

    int i;
    int j = [matriceSysteme numberOfRows];
    for(i=j-1;i>0;i--)
    {
    [[matriceSysteme cellAtRow: i column: 0] release];
    [[matriceSysteme cellAtRow: i column: 1] release];
    }

    mais je suis étonné qu'il n'y ai pas de méthodes removeCell: ou bien removeRow: .
  • BruBru Membre
    juin 2004 modifié #4
    Essaie ça :

    [tt]if([defaults boolForKey: @displayRevenus])
    † †{
    † † † /*
    † † † †*† †Revenus
    † † † †*/
    † † † [matriceSysteme addRow];
    † † † [matriceSysteme putCell: [[[NSCell alloc] initTextCell: @Revenus :] autorelease]
    † † † † † † † † † † † † † † † † †atRow: i
    † † † † † † † † † † † † † † † †column: 0];
    † † † [matriceSysteme putCell: [NSCell alloc] initTextCell: <br />† † † † †[[_systemes objectAtIndex: [mySystemesNSTableView selectedRow revenus2] autorelease]
    † † † † † † † † † † † † † † † † †atRow: i
    † † † † † † † † † † † † † † † †column: 1];
    † † † i++;
    † †}[/tt]

    La méthode alloc fournit toujours un objet avec un compteur de référence (ref count) à 1.
    La méthode autorelease marque l'objet comme quoi à la fin de la boucle d'événement, il recevra un message release.
    La méthode putCell:atRow:column: (sauf bug d'apple) fait un retain implicite sur l'objet, ce qui porte le ref count à 2.
    Lorsque la boucle d'événement se termine, le ref count retombe à 1 à cause du release de l'autorelease.
    Enfin, lorsque la ou les cellules de la matrice ne seront plus utilisées (suite à un removeXXX ou putCell:atRow:column: avec une nouvelle cellule par exemple), un release implicite sera fait. Le ref count passera alors de 1 à 0, ce qui signifira la destruction des cellules en question.

    .
  • 16:32 modifié #5
    Merci Bru, effectivement j'aurais du penser à les passer en autorelease.

    Enfin pour le moment ma méthode marche aussi pas mal et doit être plus efficace d'un point de vue CPU il me semble (beaucoup moins de passages de messages et pas recours au pool d'autorelease).
  • BruBru Membre
    16:32 modifié #6
    Je ne suis pas d'accord avec toi.

    1 - l'autoreleasepool existe et est bien là, alors pourquoi pas l'utiliser ? L'autoreleasepool a justement été créé pour ce cas précis que tu évoques.
    2 - le nombre de message ? Cela ne me semble pas être fondamentalement différent.
    3 - en un mot "autorelease" Cocoa fait ce que toi tu fais avec pas moins de 4 lignes (et evec 2 colonnes figées) et une boucle...

    Bref, pourquoi réinventer la roue, bla bla...

    .
  • 16:32 modifié #7

    2 - le nombre de message ? Cela ne me semble pas être fondamentalement différent.


    Après réflexion, tu as raison, j'avais un peu trés mal compté.


    3 - en un mot "autorelease" Cocoa fait ce que toi tu fais avec pas moins de 4 lignes (et avec 2 colonnes figées) et une boucle...


    Ben disons que actuellement dans mon code ma solution prend moins de caractères puisque j'ai une fois ces 4 lignes au lieu d'avoir 10 ou 20 fois le passage de l'autorelease.


    1 - l'autoreleasepool existe et est bien là, alors pourquoi pas l'utiliser ? L'autoreleasepool a justement été créé pour ce cas précis que tu évoques.


    Ok, ok, j'abandonnes tu as raison :o), je ferais le changement quand j'aurais un peu de temps où quand j'ajouterais une troisième colonne. Ce qui m'amène d'ailleurs à une autre question est-il vraiment impossible dans une NSMatrix d'avoir des colonnes de taille différentes ?
  • BruBru Membre
    juin 2004 modifié #8
    Ce qui m'amène d'ailleurs à une autre question est-il vraiment impossible dans une NSMatrix d'avoir des colonnes de taille différentes ?


    Oui, impossible avec NSMatrix...

    Mais à ce propos, as tu une raison particulière d'utiliser un NSMatrix plutôt qu'un NSTableView pour afficher tes données ? Car avec NSTableView, tu disposes de plus de liberté pour l'affichage de tes infos (comme justement la taille des colonnes)...

    .
  • 16:32 modifié #9
    Alors, il y a deux raisons qui font que j'ai utilisé un NSMatrix :

    1?) pour essayer et voir ce que l'on peut faire avec :-)
    2?) juste avant j'ai utilisé un NSTableView, mais son affichage est trop intrusif je trouve, ce que j'aime dans l'affichage du NSMatrix telle que je l'utilise actuellement c'est qu'il n'y a pas de différence entre le NSMatrix et une dizaine de NSTextView (ce que j'utilisais avant de faire un NSMatrix).

    De plus je compte rajouter une troisième colonne qui contiendrait uniquement une image, une LED rouge, orange ou verte comme dans les préférences systèmes réseaux où la LED donne une indication visuelle immédiate de l'état de chaque réseau. Je ne pense pas qu'un NSTableView le permette non ? Et c'est aussi ce qui m'amène à l'idée de redimensionner mon NSMatrix, sinon je compte faire un deuxième NSMatrix d'une colonne et le coller à coté du premier.
  • 16:32 modifié #10
    Une capture d'écran de mon appli est dispo là : http://alliancesunies.free.fr/captureWRP2.png

    C'est juste une petite appli pour aller avec le jeu Weymery : http://www.weymery.com

    PS : j'oubliais, une de mes contraintes est d'être compatible avec GNUstep vu que je compte la porter ensuite sous GNUstep (ce qui explique par exemple que pour la gestion des préférences je n'utilise pas le NSUserDefaultsController).
  • BruBru Membre
    16:32 modifié #11
    Tout ce que tu peux faire avec NSMatrix, tu peux le faire avec NSTableView, puisque l'un comme l'autre fonctionnent (pour l'affichage) de la même manière, à savoir l'utilisation de NSCell pour afficher les contenus.

    NSMatrix a surtout était conçu pour afficher des matrices de contrôle, dont l'exemple le plus concret sont les boutons radio. Une dérivation de NSMatrix existe : NSForm qui permet de créer des formulaires, c'est à dire des champs texte éditables précédés d'un titre.

    Selon mon avis, NSTableView est plus complexe à maitriser dans le cas de ton projet, mais par la suite, c'est cette classe qui offrira le plus de souplesse : en effet, c'est à toi qu'incombe la gestion du contenu de la matrice (cf tes soucis de désallocation), alors qu'une table se charge entièrement de ça pour peu que tu lui ofrre un objet implémentant les 2 célèbres méthodes datasource.

    .
  • juin 2004 modifié #12
    dans 1087302737:
    De plus je compte rajouter une troisième colonne qui contiendrait uniquement une image, une LED rouge, orange ou verte comme dans les préférences systèmes réseaux où la LED donne une indication visuelle immédiate de l'état de chaque réseau. Je ne pense pas qu'un NSTableView le permette non ? Et c'est aussi ce qui m'amène à l'idée de redimensionner mon NSMatrix, sinon je compte faire un deuxième NSMatrix d'une colonne et le coller à coté du premier.


    Sans le moindre problème, il suffit que tu définisses la dataCell de la colonne comme étant une NSImageCell et le tour est joué (la célèbre méthode du datasource objectValueForTableColumn... doit alors renvoyer une image). Comme tu le fais à "l'ancienne", tu n'as qu'à mettre dans le awakeFromNib de ton contrôleur:
    [[maTable columnWithIdentifier:@&quot;id&quot;] setDataCell:[[[NSImageCell alloc] init] autorelease]];
    


    J'appuie aussi ce que dis Bru, la TableView est nettement plus adaptée que la NSMatrix. N'oublie pas que tu peux changer les attributs et supprimer les bordures, les entêtes de colonnes et la couleur du fond ;).

    PS: très bonne idée que cette "rétro compatiblité GNUStep ;)"
  • 16:32 modifié #13
    Bon alors tout d'abord pour la capture d'écran je m'étais planté celle-<a href="ttp://alliancesunies.free.fr/captureWRP.png">ci</a> est la bonne. La précédente ne donnait qu'une capture de la fenêtre de gestion des préférences.

    Le problème que je vis à utiliser un NSTableView à la place de mon NSMatrix est que je vais devoir "rajouter un étage". Je m'explique dans mon NSMatrix j'alloue chaque cellule en fonction d'un test sur mon NSUserDefaults (pour n'afficher que les cellules voulues par l'utilisateur, voir le code donner plus haut dans le topic).

    Avec un NSTableView mon code va être dans ce style :
    <br />-(id)                                    † † † tableView: (NSTableView*) aTableView <br />               objectValueForTableColumn: (NSTableColumn*) aTableColumn <br />                 † †                     † † † † row: (int) rowIndex<br />{<br />        return [[anArray objectAtIndex: rowIndex] nom];<br />}<br />
    


    Si je veux faire la même chose avec un NSTableView il va falloir que je crée un nouvel NSArray qui serait rempli suivant ce qui est défini dans NSUserDefaults et ce pour chacune des entités que j'affiche (il faut voir la capture d'écran pour comprendre je pense).

    Sinon dès que j'aurais le temps et le courage je ferais une 'tite page web sur laquelle on trouvera les screenshots, le code source (GPL) et les binaires.

    En suivant un autre post dans le forum j'ai regardé le projet exemple DragNDropOutlineView qui propose la classe ImageAndTextCell qui permet d'afficher comme une seule cellule un texte et une image, j'envisage de l'utiliser plutôt que d'avoir à rajouter une troisième colonne.

    PS : pour GNUstep il faudrait que je termine l'installation du serveur X et du backend qui va avec sur mon iMac Debianisé pour pouvoir commencer à m'amuser avec.
  • 16:32 modifié #14
    dans 1087371369:
    Si je veux faire la même chose avec un NSTableView il va falloir que je crée un nouvel NSArray qui serait rempli suivant ce qui est défini dans NSUserDefaults et ce pour chacune des entités que j'affiche (il faut voir la capture d'écran pour comprendre je pense).

    En quoi cela est un problème? Tu dois aussi le faire avec les cellules de ta NSMatrix? ¿ la limite c'est même mieux, parce qu'il est plus facile de manipuler des données dans un tableau que dans des NSMatrix...(et plus cohérent vis-à-vis du MVC).

    Tout dépend aussi de la façon dont tu organises tes données. Si plutôt de mettre le système comme première dimension (autrement dis la verticale dans une table), tu mets la statitistique (par exemple les revenus) et que au sein de la même statistique, tu mets les valeurs prises par les différents pays, l'élaboration de la table devient très facile (et tu pourras même mettre une colonne par pays et donc faciliter les comparaisons...).

    dans 1087371369:

    Avec un NSTableView mon code va être dans ce style :
    <br />-(id)                                 † † † tableView: (NSTableView*) aTableView <br />               objectValueForTableColumn: (NSTableColumn*) aTableColumn <br />                 † †                     † † † † row: (int) rowIndex<br />{<br />        return [[anArray objectAtIndex: rowIndex] nom];<br />}<br />
    

    Avec ce code, tu risques bien d'avoir le même texte dans toutes les cellules d'une même ligne. N'oublie pas de rajouter un test pour vérifier dans quel colonne tu es (if ([[aTableColumn identifier] isEqualToString:@id]))

    Sinon, d'après ce que je comprends de ce que tu aimerais faire, le mieux serait une NSOutlineView. Comme ça l'utilisateur pourra choisir de développer ou non les points qu'il souhaite voir, sans devoir aller dans les préférences. Et à côté tu mets une TableView avec des checkbox pour voir quelles sont les nations qu'il veut avoir à l'écran.
  • 16:32 modifié #15


    Citation de: aLittleWoodElfe sur Aujourd'hui à 09:36:09am
    Si je veux faire la même chose avec un NSTableView il va falloir que je crée un nouvel NSArray qui serait rempli suivant ce qui est défini dans NSUserDefaults et ce pour chacune des entités que j'affiche (il faut voir la capture d'écran pour comprendre je pense).

    En quoi cela est un problème? Tu dois aussi le faire avec les cellules de ta NSMatrix? ¿ la limite c'est même mieux, parce qu'il est plus facile de manipuler des données dans un tableau que dans des NSMatrix...(et plus cohérent vis-à-vis du MVC).


    Mon appli stocke ses données dans un NSArray d'objets Systeme (oui je sais j'ai oublié de lui donner un préfixe c'est mal), mon NSMatrix  affiche pour chaque ligne le résultat d'une méthode appelée sur un objet Systeme (rappel chaque ligne n'existe que si les préférences l'autorisent, sinon la ligne n'existe pas). Ce qui donne Systeme en Modele, un Controller qui alloue les cellules que la matrice (Vue) doit afficher.

    Avec le NSTableView je suis obligé de créer un nouvel objet par Systeme (un NSArray de NSString) qui va se remplir en fonction des préférences et qui sera affiché par le NSTableView. L'autre solution étant d'intégrer à la classe Systeme ce qu'elle doit afficher en fonction des préférences mais pour moi les préférences font partie du Controller et pas du Modèle.



    Tout dépend aussi de la façon dont tu organises tes données. Si plutôt de mettre le système comme première dimension (autrement dis la verticale dans une table), tu mets la statitistique (par exemple les revenus) et que au sein de la même statistique, tu mets les valeurs prises par les différents pays, l'élaboration de la table devient très facile (et tu pourras même mettre une colonne par pays et donc faciliter les comparaisons...).


    Je tiens vraiment à mettre le système comme première dimension. Mon but n'est pas de faire des comparaisons entre les systèmes. En fait les systèmes représentent le domaine (la nation) d'un joueur, en temps normal ils sont affichés dans une page html (le rapport) généré chaque semaine (c'est un jeu tour par tour). Avec une vingtaine de systèmes le rapport commence à être trop gros et il faut pas mal scroller pour voir les informations sur chacun de ses systèmes, d'où mon idée de faire cette appli qui parse le rapport html et affiche de manière plus agréable et surtout plus rapide les différentes informations données par le rapport. J'insiste sur la rapidité de navigation, les joueurs passent pas mal de temps à discuter sur IRC et scroller tout le long de son rapport pour trouver la réponse à une question que l'on de te poser est un peu lourd.


    Sinon, d'après ce que je comprends de ce que tu aimerais faire, le mieux serait une NSOutlineView. Comme ça l'utilisateur pourra choisir de développer ou non les points qu'il souhaite voir, sans devoir aller dans les préférences. Et à côté tu mets une TableView avec des checkbox pour voir quelles sont les nations qu'il veut avoir à l'écran.


    Non ce n'est pas ça que je veux faire, en fait le comportement que j'essaie de reproduire c'est plutot celui du carnet d'adresse de Mac OS X, où on peut naviguer dans un (des ?) NSTableView uniquement au clavier et voir dans la fenêtre de droite les infos concernant la personne sélectionnée.

    Idéalement le NSTabView que l'on voit sur la capture d'écran serait remplacé par une colonne placée devant la colonne contenant le nom des différents systèmes. On aurait ainsi une première colonne contenant des groupes (Systèmes, Flottes, Plans, ...) et une deuxième colonne contenant les noms de chaque entité de ce groupe. Et à droite de tout ça l'affichage des caractéristiques de l'entité sélectionnée.

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