Encore mon problème d'array qui se dupliquent
Veillard
Membre
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).
Au secours, je sèche
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 /> ==> dans - (id)init<br /><br /> Carnet.m: [profilProfondeur release];<br /> ==> dans - (void)dealloc<br /><br /> Carnet.m: if ([profilProfondeur count] == 0)<br /> Carnet.m: profilProfondeur = [NSMutableArray arrayWithObjects: [NSNumber numberWithDouble:0.0], nil];<br /> ==> 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:@"profil profondeur"];<br /> ==> stocke l'array dans mon NSDictionary (j'ai 1 dict par plongée)<br /><br /> Carnet.m: profilProfondeur = [[tableauDesPlongees objectAtIndex:currentIndex] objectForKey:@"profil profondeur"];<br /> ==> l'inverse pour éditer ma plongée<br /><br /> Carnet.m: [profilView setProfilData: profilProfondeur];<br /> ==> accesseur pour le traçage des points dans "Profil.m"<br /><br /> Profil.h: NSMutableArray *profilProfondeurGraph, *ASC_FlagGraph, *SLOW_FlagGraph, *SURF_FlagGraph, *CEIL_FlagGraph;<br /><br /> Profil.m: id oldData = profilProfondeurGraph;<br /> ==> dans<br /> - (void)setProfilData:(NSMutableArray*)prof<br /> {<br /> id oldData = profilProfondeurGraph;<br /> profilProfondeurGraph = [prof retain];<br /> [oldData release];<br /> }<br /><br /> après j'utilise "profilProfondeurGraph" pour tracer mon graphe dans "Profil.m"
Au secours, je sèche
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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...
.
Ca ne m'étonne pas
Tout à fait ça
Je t'envoie ça, ne rigole pas, promis
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) :
et
.
Merci
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...
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.
.
Quand tu parles de "coder les méthodes", tu veux parler des différentes variables accesseurs qui seront appelées ultérieurement ?
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...
.
J'ai déjà fait l'inventaire de mes variables d'instance.
Je continue...
.
Par contre j'ai du mal à saisir quand tu écris :
J'avais utilisé un NSDictionary
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 :
j'ai écrit dans "Plongee.h"
Par contre, est-ce utile de mettre
Est-ce que :
ne suffit pas (c'est un int et non un NSNumber par ex.) ???
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 :
.
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...
.
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):
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.
.
Je ne comprends pas trop le problème...
.
Oui, ça concerne :
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...
c'est pas plus simple comme ça ? ???
Désolé, j'ai parlé trop vite. J'ai vu mon erreur p. 98 sur le livre.
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...
.
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]
.
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.
.
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 )
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
Juste une question : "moncarnet" dans
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-).
.