Calcul d'age + affichage dans une NSTableView

AkisAkis Membre
11:34 modifié dans API AppKit #1
Bonjour,

Je réalise une petite application permettant de récupérer les contacts de l'Address Book et de calculer leur age.

Voici la façon donc je calcule l'age. (birthday étant une variable contenant la date de naissance d'une personne. Egalement de type NSCalendarDate).

NSCalendarDate *today = [NSCalendarDate date];<br />	int years;<br />	[today years:&amp;years months:NULL days:NULL hours:NULL minutes:NULL seconds:NULL sinceDate:birthday];<br />		<br />	age = [NSString stringWithFormat:@&quot;%d&quot;, years];


Cela fonctionne parfaitement, il calcule l'age comme il faut. La variable "age" contient bien l'age car si je vérifie son contenu ou que je l'affiche dans la console, le rendu est correct.

Cet age, je l'insère après dans une NSTableView. Cela fonctionne à  nouveau parfaitement SAUF pour une personne ayant une date de naissance en 1927.
L'age est bien calculé mais le rendu dans la NSTableView n'est pas correct. Il m'affiche ceci (lorsque l'application ne plante pas d'ailleurs) : picture1tu6.png

Et à  première vue, cela refuse d'afficher la bonne valeur uniquement pour cette année, si je change en 1928 ou 1926, l'age est correctement affiché dans la vue.

Quelqu'un aurait une idée du problème ?

Merci d'avance.

Réponses

  • schlumschlum Membre
    11:34 modifié #2
    ça ressemble à  un arrosage mémoire, mais il n'y a pas assez de détails pour en dire plus.
  • AkisAkis Membre
    11:34 modifié #3
    Qu'est ce qu'un arrosage mémoire ? Et que faut-il comme détails afin de régler ce soucis ?
  • AliGatorAliGator Membre, Modérateur
    11:34 modifié #4
    Moi je pense plutôt à  un problème d'encodage : de par l'âge de la personne, ça doit commencer à  rentrer dans les intervalles UTF-8 des caractères bizarre par exemple, d'où l'affichage peut-être ?
    Ou alors tu as converti ton âge, entier, d'une mauvaise façon pour l'afficher dans ta tableView, ce qui vait que pour les petites valeurs ça va mais pour les plus grandes ça fait n'importe quoi.

    Tu pourrais nous mettre le code de tes méthodes implémentant le protocole NSTableDataSource ? Qu'on voit comment tu récupères ton age de type int et comment tu le convertit en chaà®ne pour le renvoyer à  la DataSource ? A moins que tu ne le convertisses pas ?...
  • schlumschlum Membre
    11:34 modifié #5
    dans 1225642995:

    Qu'est ce qu'un arrosage mémoire ? Et que faut-il comme détails afin de régler ce soucis ?


    Un arrosage mémoire c'est quand on écrit dans de la mémoire qui n'a pas été allouée ou qui ne nous appartient pas...
    ça donne des résultats aléatoires en général mais ça peut aboutir à  un crash de l'application (particulièrement quand on écrit sur la pile d'exécution du programme).

    Souvent, quand on a des crashs aléatoires pour les mêmes manipulation, c'est ça.
    Il faut alors voir au débugueur où ça a planté et ce qu'on a fait juste avant... (le problème c'est que ça peut crasher longtemps après !)
  • AkisAkis Membre
    11:34 modifié #6
    dans 1225645243:

    ...

    Pour ce qui est du code qui calcule l'age, il est dans le premier message. Et en effet, c'est un entier que je converti en string pour l'afficher dans ma NSTableView.

    Ce qui est étrange, c'est que si j'ai une année en 1928 ou 1926, j'aurais bien l'affichage qu'il faut (80 ou 82 ans). J'ai le soucis qu'avec l'année 1927, du moins, j'ai l'impression. Donc ça n'est pas un problème de "valeur trop grande".

    dans 1225646319:

    Un arrosage mémoire c'est quand on écrit dans de la mémoire qui n'a pas été allouée ou qui ne nous appartient pas...
    ça donne des résultats aléatoires en général mais ça peut aboutir à  un crash de l'application (particulièrement quand on écrit sur la pile d'exécution du programme).

    Souvent, quand on a des crashs aléatoires pour les mêmes manipulation, c'est ça.
    Il faut alors voir au débugueur où ça a planté et ce qu'on a fait juste avant... (le problème c'est que ça peut crasher longtemps après !)

    Merci de l'info.

    Le soucis aussi, c'est que j'avais bien évidemment tester le debuggeur. Ce qui fait qu'il inscrit bien l'age comme il faut dans la variable. (De plus, j'ai doublement vérifier en l'affichant dans la console avec un NSlog(). Ce qui l'affiche bien également).
    A première vue, je ne sais pas ce que fait mon NSTableView mais il aime pas trop le chiffre 81 :/

    Et je viens encore de vérifier, après avoir créer mon NSArray avec mes éléments, j'ai vérifier le contenu de chaque entrée dans le debuggeur et tout est correct. Chaque variable affichée dans le NSTableView est bien remplie comme il faut.

    Donc à  première vue, c'est quand je met le tableau dans mon NSTableView ?
    J'utilise un NSArrayController pour ce faire avec la méthode suivante :
    [arrController addObjects:arrPersons];
    
  • Philippe49Philippe49 Membre
    11:34 modifié #7
    dans 1225657385:

    Donc à  première vue, c'est quand je met le tableau dans mon NSTableView ?
    J'utilise un NSArrayController pour ce faire avec la méthode suivante :
    [arrController addObjects:arrPersons];
    



    Tu utilises un array controller et tu gères la Table View en Data Source? en Binding ? en CoreData ?
     
  • AkisAkis Membre
    11:34 modifié #8
    En binding.
  • Philippe49Philippe49 Membre
    11:34 modifié #9
    Alors il est plus prudent de suivre le modèle donné à  ce post (ou celui plus ancien que Les NSArray Controller à  la fête)

    Le principe est de dissocier le modèle du controller en mettant ce modèle (=les données) dans une NSMutableArray.
    Tu places ce NSMutableArray dans l'une de tes classes principales, et tu fais les connexions via le NSArrayController entre la table view et le modèle.
  • AkisAkis Membre
    11:34 modifié #10
    Merci, je vais regarder tout cela.
  • Philippe49Philippe49 Membre
    novembre 2008 modifié #11
    Mon idée personnelle sur les bindings est de les réserver à  des situations relativement simples. Si un programme doit prendre de l'ampleur, le bon vieux système du data source reste toujours plus facile à  débugger et à  faire évoluer. Mais il faut prendre cela comme un avis personnel.

  • schlumschlum Membre
    11:34 modifié #12
    Mon avis personnel à  moi étant de ne jamais utiliser les bindings  :P
  • AkisAkis Membre
    11:34 modifié #13
    Est-ce que vous avez aussi un tutoriel sur les datasources ?
    Ainsi, je pourrai tester les deux.
  • Philippe49Philippe49 Membre
    11:34 modifié #14
    ADC Home > Reference Library > Guides > Cocoa > User Experience > Table View Programming Guide >

    ADC Home > Reference Library > Reference > Cocoa > User Experience > NSTableDataSource Protocol Reference >
  • AkisAkis Membre
    11:34 modifié #15
    Pour les datasources, ça permet vraiment plus de chose à  première vue... Merci de l'info ;)

    J'ai essayé de l'implémenter pour mon petit programme mais ça coince quelque part.

    Niveau code, j'ai lu qu'il fallait implémenter 3 méthodes, chose que j'ai fais, correctement je pense. :
    - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex {<br />	return [[model objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]];<br />}<br />- (void)tableView:(NSTableView *)aTableView setObjectValue:anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex {<br />&nbsp; &nbsp; [[model objectAtIndex:rowIndex] setObject:anObject forKey:[aTableColumn identifier]];<br />}<br />- (int)numberOfRowsInTableView:(NSTableView *)aTableView {<br />&nbsp; &nbsp; return [model count];<br />}
    


    (model correspond à  mon NSArray)

    Ensuite, j'ai toujours mon algo qui me rempli mon NSArray :
    model = [[NSArray alloc] init];<br />	for(ABPerson *abp in people) {<br />		NSString *name = [abp valueForKey:@&quot;First&quot;];<br />		NSCalendarDate *birthday = [abp valueForKey:@&quot;Birthday&quot;];		<br />		NSString *age = [self getAgeString:birthday];<br />		<br />		NSMutableDictionary *person = [NSMutableDictionary dictionaryWithObjectsAndKeys:name,@&quot;name&quot;,birthday,@&quot;birthday&quot;,age,@&quot;age&quot;, nil];<br />		model = [model arrayByAddingObject:person];<br />		<br />		[name release];<br />		[birthday release];<br />		[age release];<br />		[person release];<br />	}
    


    Du côté d'IB, j'ai bien ma class qui référence le datasource et delegate. Les deux vers la TableView.

    Et du côté de la table view, mes colonnes sont identifiées par les string : name, birthday et age.


    Je n'ai pas l'impression d'avoir fait des erreurs, pourtant cela ne fonctionne pas.

    Lorsque je lance mon appli (avec des NSLog un peu partout), je m'aperçois qu'au lancement de l'appli, numberOfRowsInTableView me renvoie 0. Après seulement est générer mon model mais l'appli semble s'arrêter là ... :/

    Merci :)
  • Philippe49Philippe49 Membre
    11:34 modifié #16
    Une remarque annexe d'abord
    dans 1225734217:

    model = [model arrayByAddingObject:person];

    en utilisant une NSMutableArray, tu allégerais (au niveau fonctionnement) en  [model addObject:person];

    dans 1225734217:

    Lorsque je lance mon appli (avec des NSLog un peu partout), je m'aperçois qu'au lancement de l'appli, numberOfRowsInTableView me renvoie 0. Après seulement est générer mon model mais l'appli semble s'arrêter là ... :/
    Merci :)

    [tableView reloadData] doit être fait (en tout ou partie) après tout changement sur le modèle, afin que la vue en prenne connaissance.

  • AkisAkis Membre
    novembre 2008 modifié #17
    Effectivement, c'était bien mon rechargement de la tableView qu'il fallait que je fasse...

    Mon count me renvoie maintenant une valeur incrémentée à  chaque fois. Donc c'est bon.

    Par contre, j'ai l'impression que les 2 autres méthodes ne sont pas correct car elles ne sont "jamais" appelée. J'ai des NSLog dans chacune d'elle et je n'ai rien dans la console.


    Remarque: si j'utilise un NSMutableArray, il me renvoie cette erreur : [NSCFArray insertObject:atIndex:]: mutating method sent to immutable object

    //edit: cette erreur là  : "objc[10594]: FREED(id): message release sent to freed object=0x1c4a00", cela peut signifier quoi ?
  • Philippe49Philippe49 Membre
    11:34 modifié #18
    dans 1225741389:

    Remarque: si j'utilise un NSMutableArray, il me renvoie cette erreur : [NSCFArray insertObject:atIndex:]: mutating method sent to immutable object

    ça c'est un problème dans la déclaration ou création

    déclaration
    NSMutableArray * model;

    initialisation par exemple juste après que le nib ait été chargé
    -(void) awakeFromNib {
        model=[[NSMutableArray alloc] init];
      ...
    }
    dans 1225741389:

    //edit: cette erreur là  : "objc[10594]: FREED(id): message release sent to freed object=0x1c4a00", cela peut signifier quoi ?

    C'est un problème de retain : la création d'un objet n'assure pas une durée de vie permanente ... peut-être as-tu utilisé
    model=[NSMutableArray array]; qui n'assure pas un retainCount >=1


    dans 1225741389:

    Par contre, j'ai l'impression que les 2 autres méthodes ne sont pas correct car elles ne sont "jamais" appelée. J'ai des NSLog dans chacune d'elle et je n'ai rien dans la console.


    Là  cela vient peut-être des erreurs précédentes.

    Remarque : la méthode setObjectValue sert uniquement lors d'un changement provoqué par l'utilisateur dans la table view.








  • AkisAkis Membre
    novembre 2008 modifié #19
    Hmm effectivement, si je ne change pas le type à  l'initialisation, normal que ça ne fonctionne pas ^^ Merci ;)

    Concernant l'erreur de Freed(id) ... , j'ai du retirer tous mes releases pour qu'ils ne l'affichent plus :/

    Il y a les release que l'on peut voir dans le code de ce post et j'engloble tout mon code d'un NSAutoreleasePool.
    J'avais même pendant un moment mis dans ma boucle un [pool drain] comme j'avais vu, je ne sais plus où, que cela était utile dans de le faire dans une boucle.


    Sinon merci pour tout, ça fonctionne et je n'ai plus mon "bug" du fameux chiffre 81 qu'il ne voulait pas inscrire sans bugger :)

    //edit : Et je viens de tester avec les bindings aussi et ça fonctionne maintenant, je devais avoir une erreur quelque part que j'avais pas vu ^^ Cependant, je vais garder ma solution avec le datasource car ça me permet de faire plus de chose je trouve.
Connectez-vous ou Inscrivez-vous pour répondre.