Encore mon problème d'array qui se dupliquent

VeillardVeillard Membre
15:52 modifié dans API AppKit #1
Bonjour,

Après des essais infructueux malgré l'aide de nombreux membres de la liste, je remets sur le tapis mon problème de NSMutableArray partagés. Cette fois, je vais essayer d'être plus clair et d'apporter plus d'infos.  :-\\

Le problème est le suivant :
J'ai une liste de plongées dans un NSTableView. Chaque plongée contient divers renseignements sous forme de NSString et NSNumber essentiellement.
Puis, j'ai rajouté pour chaque plongée, des données de profil (ensemble de points permettant de te tracer une courbe montrant l'évolution de la profondeur en fonction du temps). Ces points sont stockés dans des NSMutableArray.

Quand je je créée une plongée dans ma liste, pas de problème, aucune courbe n'apparaà®t puisque puisque le NSMutableArray de la plongée créée ne contient pas de points.
Ce qui est étrange, c'est quand je clique sur une des plongées dans ma liste et que je créée dans le foulée une nouvelle plongée, la plongée créée hérite des points de la plongée qui a été préalablement éditée...
J'ai essayé de vider l'array à  la création de la nouvelle plongée et là , celle d'avant a perdu son profil.  :'(

L'array s'appelle "profilProfondeur", il se trouve dans "Carnet.m", j'ai un accesseur qui permet d'utiliser les données de cet array dans "Profil.m" sous le nom de "profilProfondeurGraph" (voir l'extrait ci-dessous).

Carnet.h: 	NSMutableArray	*profilProfondeur, *ASC_Flag, *SLOW_Flag, *SURF_Flag, *CEIL_Flag;<br /><br />	Carnet.m: 	profilProfondeur = [[NSMutableArray alloc] init]; <br />			==&gt; dans - (id)init<br /><br />	Carnet.m: 	[profilProfondeur release];<br />			==&gt; dans - (void)dealloc<br /><br />	Carnet.m: 	if ([profilProfondeur count] == 0)<br />	Carnet.m: 		profilProfondeur = [NSMutableArray arrayWithObjects: [NSNumber numberWithDouble:0.0], nil];<br />			==&gt; je stocke une valeur mini par défaut si pas de profil pour éviter un plantage lors du tracé de la courbe.<br /><br />	Carnet.m: 	[plongeesDict setObject: profilProfondeur forKey:@&quot;profil profondeur&quot;];<br />			==&gt; stocke l&#39;array dans mon NSDictionary (j&#39;ai 1 dict par plongée)<br /><br />	Carnet.m: 	profilProfondeur = [[tableauDesPlongees objectAtIndex:currentIndex] objectForKey:@&quot;profil profondeur&quot;];<br />			==&gt; l&#39;inverse pour éditer ma plongée<br /><br />	Carnet.m: 	[profilView setProfilData: profilProfondeur];<br />			==&gt; accesseur pour le traçage des points dans &quot;Profil.m&quot;<br /><br />	Profil.h: 	NSMutableArray *profilProfondeurGraph, *ASC_FlagGraph, *SLOW_FlagGraph, *SURF_FlagGraph, *CEIL_FlagGraph;<br /><br />	Profil.m: 	id oldData = profilProfondeurGraph;<br />			==&gt; dans<br />						- (void)setProfilData:(NSMutableArray*)prof<br />						{<br />							id oldData = profilProfondeurGraph;<br />							profilProfondeurGraph = [prof retain];<br />							[oldData release];<br />						}<br /><br />	après j&#39;utilise &quot;profilProfondeurGraph&quot; pour tracer mon graphe dans &quot;Profil.m&quot;


Au secours, je sèche  :(
«1

Réponses

  • BruBru Membre
    15:52 modifié #2
    Le peu de code que tu as mis me hérisse le poil...

    En fait, détaillons les données et leurs relations :
    - tu as 1 carnet de plongée.
    - 1 carnet de plongée contient X plongées.
    - 1 plongée contient 1 profil de plongée.

    En conclusion, 1 carnet de plongée stocke (indirectement) X profils de plongée.

    Or tu déclares (variable d'instance) et tu alloues (méthode init) le NSArray du profil des plongée comme pour le carnet de plongée : donc tu as 1 carnet de plongée avec 1 profil de plongée.

    C'est pas étonnant que toutes les plongées de ton carnet partage le même profil (techniquement, tu utilises le même objet pour chaque plongée).

    J'aimerai connaà®tre le code que tu utilises lorsque tu déclares/créés une nouvelle plongée que tu ajoutes dans le cranet...

    .
  • VeillardVeillard Membre
    15:52 modifié #3
    Le peu de code que tu as mis me hérisse le poil...

    Ca ne m'étonne pas  :o


    En fait, détaillons les données et leurs relations :
    - tu as 1 carnet de plongée.
    - 1 carnet de plongée contient X plongées.
    - 1 plongée contient 1 profil de plongée.

    Tout à  fait ça

    J'aimerai connaà®tre le code que tu utilises lorsque tu déclares/créés une nouvelle plongée que tu ajoutes dans le cranet...

    Je t'envoie ça, ne rigole pas, promis  B)
  • BruBru Membre
    15:52 modifié #4
    Bon, j'ai un peu regardé...

    Sincèrement, je pense que tu as fauté dans le sens où tu as mélangé données fondamentales et UI (via les classes contrôleurs)...

    Pour moi, tu devrais avoir une classe PLONGEE et une classe CARNET indépendantes toutes 2 (dérivant de NSObject). En faisant ça, tu vois tout de suite ce qui appartient à  CARNET, ce qui appartient à  PLONGEE...

    Par exemple (selon ton code) :
    <br />@interface Carnet : NSObject<br />{<br />&nbsp; &nbsp; NSMutableArray&nbsp; *tableauDesPlongees;<br />}<br />
    


    et
    <br />@interface Plongee : NSObject<br />{<br />&nbsp; &nbsp; int nbPlongee;<br />&nbsp; &nbsp; NSDate *date;<br />&nbsp; &nbsp; float coeff;<br />&nbsp; &nbsp; NSString *pmBm;<br />&nbsp; &nbsp; NSString *lieu;<br />&nbsp; &nbsp; NSString *site;<br />&nbsp; &nbsp; NSString *palanquee;<br />&nbsp; &nbsp; NSString *epaves;<br />&nbsp; &nbsp; NSString *bateau;<br />&nbsp; &nbsp; NSMutableArray&nbsp; *profilProfondeur;<br />}<br />
    


    .
  • VeillardVeillard Membre
    15:52 modifié #5
    OK je vais essayer de refondre tout ça  :boss):
    Merci
  • VeillardVeillard Membre
    15:52 modifié #6
    C'est l'horreur, j'ai plantages sur plantages, je ne sais pas par quel bout commencer  :'( :'( :'(
  • muqaddarmuqaddar Administrateur
    15:52 modifié #7
    Courage favouille...
    C'ets toujours comme ça quand on revoit la structure de son appli...
    ça m'est arrivé, j'hésitais pas à  commenter de gros morceaux de code pour voir ce qui allait pas...
  • BruBru Membre
    15:52 modifié #8
    Tout d'abord, il ne faut pas toucher à  la structure actuelle de l'appli (on verra ça plus tard).

    Pour le moment, tu construits tes classes, et surtout tu codes les méthodes que tu auras besoin...

    Une fois tout ça terminé, là  tu commenceras à  remplacer tes arrays et tes dictionarys par des appels à  tes nouvelles classes.

    .
  • VeillardVeillard Membre
    15:52 modifié #9
    OK, j'avais commençé par la fin.
    Quand tu parles de "coder les méthodes", tu veux parler des différentes variables accesseurs qui seront appelées ultérieurement ?
  • BruBru Membre
    15:52 modifié #10
    Entre autre oui...

    Par exemple, pour la classe CARNET, je vois quelques chose comme ça :

    - (void)addPlongee:(PLONGEE *)plongee;
    - (PLONGEE *)plongeeAtNumber:(int)number;
    - (void)removePlongeeAtNumber:(int)number;
    - (void)insertPlongee:(PLONGEE *)plongee atNumber:(int)number;
    etc...

    Et pour PLONGEE :
    - (void)setDate:(NSDate *)date;
    - (void)setLieu:(NSString *)lieu;
    - (void)setCoeff:(float)coeff;
    - (NSDate *)date;
    - (NSString *)lieu;
    - (float)coeff;
    - (NSArray *)profil;
    etc...

    .
  • VeillardVeillard Membre
    15:52 modifié #11
    Merci Bru.

    J'ai déjà  fait l'inventaire de mes variables d'instance.
    Je continue...  :)
  • BruBru Membre
    15:52 modifié #12
    N'oublie pas non plus les méthodes de codage/archivage (et leurs inverses) pour stocker ensuite ton carnet (avec ses plongées et ses profils) sur disque.

    .
  • VeillardVeillard Membre
    15:52 modifié #13
    OK, je vérifie tout ça...
  • VeillardVeillard Membre
    15:52 modifié #14
    Bon, ça y est, j'ai codé les méthodes pour plus de 60 variables.  :)

    Par contre j'ai du mal à  saisir quand tu écris :
    - (void)addPlongee:(PLONGEE *)plongee;
    


    J'avais utilisé un NSDictionary
    - (NSDictionary *)createDive;
    

    avec toutes les clés à  l'intérieur.

    Ce qui m'inquiète le plus, c'est le stockage sur disque. J'espère que je peux réutiliser mes anciens fichiers ? (XML)

    Autre chose, par exemple pour :

    - (void)setDate:(NSDate *)myDate;<br />- (NSDate *)date;
    


      j'ai écrit dans "Plongee.h"

    - (void)setDate:(NSDate *)myDate<br />{<br />	id oldData = date;<br />	date = [myDate retain];<br />	[oldData release];<br />}<br /><br />- (NSDate *)date<br />{<br />	return date;<br />}<br />
    


    Par contre, est-ce utile de mettre

    - (void)setCoeff:(int)myCoeff;<br />- (int)coeff;<br />
    


    Est-ce que :

    - (int)coeff<br />{<br />	return coeff;<br />}<br />
    


    ne suffit pas (c'est un int et non un NSNumber par ex.)  ???
  • BruBru Membre
    15:52 modifié #15
    dans 1104486642:
    Par contre j'ai du mal à  saisir quand tu écris :
    - (void)addPlongee:(PLONGEE *)plongee;
    

    J'avais utilisé un NSDictionary
    - (NSDictionary *)createDive;
    

    avec toutes les clés à  l'intérieur.


    Tu fais comme tu veux. Là  ce n'est pas important.
    Même si la formulation addPlongee est plus proche de l'esprit cocoa. Par exemple,voici ce que cela donnerai :
    <br />{<br />    PLONGEE *plongee;<br /><br />    plongee=[[PLONGEE alloc] init];<br />    [plongee setProfonfeur:18.3];<br />    [plongee setPalanquee:[NSArray arrayWithObjects:@&quot;Bruno&quot;, @&quot;Catherine&quot;, nil]];<br />    [plongee setDuree:43];<br />    [plongee setCommentaire:@&quot;congre, vieille, girelle paon, mur de corynactis&quot;];<br />    [moncarnet addPlongee:plongee];<br />    [plongee release];<br />}
    


    .
  • BruBru Membre
    15:52 modifié #16
    dans 1104486642:

    Ce qui m'inquiète le plus, c'est le stockage sur disque. J'espère que je peux réutiliser mes anciens fichiers ? (XML)


    Quoiqu'il en soit, ça sera toujours récupérable (en effet, il faudra prévoir ton ton logiciel une section d'import export, afin notamment de récupérer des données provenant d'autres softs de plongée).

    Maintenant, se pose la question du volume des données, pour lequel le format XML ne s'adapte plus trop...

    .
  • BruBru Membre
    janvier 2005 modifié #17
    dans 1104486642:

    Autre chose, par exemple pour :
    - (void)setDate:(NSDate *)myDate;<br />- (NSDate *)date;
    

    j'ai écrit dans "Plongee.h"
    - (void)setDate:(NSDate *)myDate<br />{<br />	id oldData = date;<br />	date = [myDate retain];<br />	[oldData release];<br />}<br />- (NSDate *)date<br />{<br />	return date;<br />}<br />
    



    Je préfère (mais ce n'est qu'affaire de goût), une structure comme celle-là  (je nommerai _date la variable d'instance contenant la date dans l'objet):
    <br />- (void)setDate:(NSDate *)myDate<br />{<br />    [_date release];<br />    _date=[[NSDate alloc] initWithTimeIntervalSinceNow:[myDate timeIntervalSinceNow]];<br />}<br /><br />- (NSDate *)date<br />{<br />    return [NSDate dateWithTimeIntervalSinceNow:[_myDate timeIntervalSinceNow]];<br />}<br />
    


    Ici, on mémorise toujours une copie de la date passée en paramètre. De même, on retourne une copie de la date mémorisé dans l'instance de classe.

    .
  • BruBru Membre
    15:52 modifié #18
    dans 1104486642:

    Par contre, est-ce utile de mettre
    - (void)setCoeff:(int)myCoeff;<br />- (int)coeff;<br />
    

    Est-ce que :
    - (int)coeff<br />{<br />	return coeff;<br />}<br />
    

    ne suffit pas (c'est un int et non un NSNumber par ex.) ???


    Je ne comprends pas trop le problème...

    .
  • VeillardVeillard Membre
    15:52 modifié #19
    Bonjour Bru et bonne année  :p

    Oui, ça concerne :
    - (void)setCoeff:(int)myCoeff;
    

    Je ne sais pas quoi mettre dedans. ???
    Dans le livre "Cocoa par la pratique", il n'y a pas ce type de méthode pour les int et les float...
  • Eddy58Eddy58 Membre
    15:52 modifié #20
    Et bien comme ce n'est pas un objet, tu changes tout simplement l'ancienne valeur de coeff par la nouvelle : :)

    <br />- (void)setCoeff:(int)myCoeff<br />{<br />coeff=myCoeff;<br />}<br />
    
  • cbrandtcbrandt Membre
    15:52 modifié #21
    dans 1104688697:

    Je préfère (mais ce n'est qu'affaire de goût), une structure comme celle-là  (je nommerai _date la variable d'instance contenant la date dans l'objet):
    <br />- (void)setDate:(NSDate *)myDate<br />{<br />    [_date release];<br />    _date=[[NSDate alloc] initWithTimeIntervalSinceNow:[myDate timeIntervalSinceNow]];<br />}<br /><br />- (NSDate *)date<br />{<br />    return [NSDate dateWithTimeIntervalSinceNow:[_myDate timeIntervalSinceNow]];<br />}<br />
    


    Ici, on mémorise toujours une copie de la date passée en paramètre. De même, on retourne une copie de la date mémorisé dans l'instance de classe.

    .


    c'est pas plus simple comme ça ? ???

    <br />- (void)setDate:(NSDate *)myDate<br />{<br />    [_date release];<br />    _date=[myDate copy];<br />}<br /><br />- (NSDate *)date<br />{<br />    return [_date copy];<br />}<br />
    
  • VeillardVeillard Membre
    15:52 modifié #22
    Et bien comme ce n'est pas un objet, tu changes tout simplement l'ancienne valeur de coeff par la nouvelle  :)

    Désolé, j'ai parlé trop vite. J'ai vu mon erreur p. 98 sur le livre.  :o :o :o
  • BruBru Membre
    15:52 modifié #23
    dans 1104693911:

    c'est pas plus simple comme ça ? ???


    Toutes les classes ne se conforment pas complètement au protocole copying. Alors je fais mes propres copies, ça coûte pas trop de code en plus et ça me rassure...

    .
  • VeillardVeillard Membre
    15:52 modifié #24
    Donc, on peut faire quelque chose du style :

    - (void)setLieu:(NSString *)myLieu<br />{<br />&nbsp; &nbsp; [_lieu release];<br />&nbsp; &nbsp; _lieu=[[NSString alloc] initWithString:@&quot;&quot;];<br />}<br /><br />- (NSString *)lieu<br />{<br />&nbsp; &nbsp; return [NSString stringWithString:@&quot;&quot;];<br />}
    


    Pour les NSString ?
  • BruBru Membre
    15:52 modifié #25
    dans 1104697326:

    Donc, on peut faire quelque chose du style :
    - (void)setLieu:(NSString *)myLieu<br />{<br />    [_lieu release];<br />    _lieu=[[NSString alloc] initWithString:@&quot;&quot;];<br />}<br />- (NSString *)lieu<br />{<br />    return [NSString stringWithString:@&quot;&quot;];<br />}
    

    Pour les NSString ?


    Oui, c'est comme ça que je fais (sauf que dans ton code, il faut remplacer les @";" par _lieu ou myLieu) :
    [tt]- (void)setLieu:(NSString *)myLieu
    {
    [_lieu release];
    _lieu=[[NSString alloc] initWithString:myLieu];
    }
    - (NSString *)lieu
    {
    return [NSString stringWithString:_lieu];
    }[/tt]

    .
  • BaardeBaarde Membre
    15:52 modifié #26
    dans 1104688697:
    Je préfère (mais ce n'est qu'affaire de goût), une structure comme celle-là  (je nommerai _date la variable d'instance contenant la date dans l'objet):
    <br />- (void)setDate:(NSDate *)myDate<br />{<br />    [_date release];<br />    _date=[[NSDate alloc] initWithTimeIntervalSinceNow:[myDate timeIntervalSinceNow]];<br />}<br /><br />- (NSDate *)date<br />{<br />    return [NSDate dateWithTimeIntervalSinceNow:[_myDate timeIntervalSinceNow]];<br />}<br />
    


    Ici, on mémorise toujours une copie de la date passée en paramètre. De même, on retourne une copie de la date mémorisé dans l'instance de classe.

    .

    Avec quelques dates et chaà®nes ça va mais si tout le monde fait comme ça, je comprends où file ma mémoire...  ;)
  • BruBru Membre
    janvier 2005 modifié #27
    dans 1104706292:

    Avec quelques dates et chaà®nes ça va mais si tout le monde fait comme ça, je comprends où file ma mémoire... ;)


    Ouais, surtout quand on sait pas utiliser release ou autorelease correctement !

    Bref, même Apple s'y met : voir le bug de 10.0 ou setTitle de NSWindow ne faisait qu'un retain sur la NSString passée en paramètre. Or si cette NSString était une NSMutableString, et si par la suite dans programme, on modifiait cette chaà®ne, ça foutait bien-sûr la grouille dans la barre de titre de la fenêtre... Maintenant, c'est bien une copie de la chaà®ne qui est utilisée pour le titre.

    .
  • ClicCoolClicCool Membre
    15:52 modifié #28
    Salut,

    Je suis d'accord ++ avec Bru pour l'usage de copies dans les accesseurs.
    Celà  impose un code plus propre et interdit complètement la modification d'une variable d'instance sans passer par l'accesseur approprié.
    Ce qui me semble plus conforme à  l'encapsulation objet.

    Pour ce qui est de l'usage des retain, release et autorelease c'est un effort à  faire et une bonne gymnastique garantissant la bonne conception du code. (même si c'est pas toujours facile de repérer une fuite mémoire dans un code un peu fourni ;) )
  • ClicCoolClicCool Membre
    15:52 modifié #29
    Deux petites précisions néanmoins,

    1°) De mon coté j'ai une préférence pour les copies de variables d'instances renvoyées par mes accesseurs "getters".
    Je les renvoies toujours autoreleasées.

    D'abbord parcequ'il n'est pas question qu'un accesseur renvoie des copies simples et un autre des copies releasées, sinon on s'y perd  ???

    Ensuite parceque certains appels aux accesseurs ne sont pas directement faits par mon code.
    Par exemple pour une tableView gérée par binding et pour laquelle le nombre d'appel peut être important (beaucoup de copies faites) sans que j'ai de façon simple l'opportunité d'effectuer les releases à  postériori.
    Les onjets générant ces appels comme les arrayControllers sachant parfaitement adresser un retain (puis un release) à  la copie qu'ils reçoivent, je trouve plus propre de leur renvoyer une copie autoreleasée qui sera désalouée dès que le contrôleur n'en a plus besoin.

    Le problème est sensiblement le même avec le dataSource d'une TableView du reste.
    Il y est plus simple (dans les méthode de datasource) de renvoyer directement la copie autoreleasée du getter que d'appeler le getter puis autoreleaser la copie récupérée avant de la renvoyer pour la tableView.

    2°) Bien sur il y a des Variables d'instance qui sont plus des pointeurs vers d'autres objets (conteneurs ou délégates etc...) que des valeurs. Le getter est alors souvent appelé pour récupérer un pointeur sur un objet auquel on va adresser des méthodes. Là , le plus souvent, pas question de renvoyer une copie ;)

  • VeillardVeillard Membre
    15:52 modifié #30
    Bon, ça avance petit à  petit.  :)
    Juste une question : "moncarnet" dans
    [moncarnet addPlongee:plongee];
    

    est-il un NSMutableArray ?
    J'ai essayé et ça ne marche pas  ???
  • BruBru Membre
    15:52 modifié #31
    dans 1104783816:

    Bon, ça avance petit à  petit. :)
    Juste une question : "moncarnet" dans
    [moncarnet addPlongee:plongee];
    

    est-il un NSMutableArray ?
    J'ai essayé et ça ne marche pas ???


    Non, c'est aussi une classe dérivant de NSObject, dont la principale variable est le NSMutableArray stockant les objets PLONGEE, mais contenant aussi d'autres données (à  l'image de la première page de ton carnet de plongée FFESSM -ancien format-).

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