Message d'erreur du à NSTextFieldCell
Veillard
Membre
Bonjour,
Je suis nouveau sur la liste et dans la programmation en Cocoa. J'ai commencé à traduire une appli. je j'ai développé en Carbon et je me heurte à quelques difficultés.
Dans un premier temps, je cherche à saisir des données sous forme d'entiers ou de "strings" dans des "NSTextField" pour l'instant tout fonctionne.
Dans un second temps, je veux que ces valeurs réapparaissent dans ces "NSTextFieldCell"Â ce qui fonctionne très bien pour les entiers mais pas pour les "string".
A l'exécution de l'appli, quand je clique sur mon bouton que j'ai appelé "Editer", j'ai le message suivant qui apparaà“t dans la console d'Xcode :
2004-07-11 09:49:23.428 CocoaApp[416] *** Assertion failure in -[NSTextFieldCell _objectValue:forString:], AppKit.subproj/NSCell.m:1131
2004-07-11 09:49:23.444 CocoaApp[416] Invalid parameter not satisfying: aString != nil
A quel endroit ai-je fait un erreur ?
Merci
Je suis nouveau sur la liste et dans la programmation en Cocoa. J'ai commencé à traduire une appli. je j'ai développé en Carbon et je me heurte à quelques difficultés.
Dans un premier temps, je cherche à saisir des données sous forme d'entiers ou de "strings" dans des "NSTextField" pour l'instant tout fonctionne.
Dans un second temps, je veux que ces valeurs réapparaissent dans ces "NSTextFieldCell"Â ce qui fonctionne très bien pour les entiers mais pas pour les "string".
A l'exécution de l'appli, quand je clique sur mon bouton que j'ai appelé "Editer", j'ai le message suivant qui apparaà“t dans la console d'Xcode :
2004-07-11 09:49:23.428 CocoaApp[416] *** Assertion failure in -[NSTextFieldCell _objectValue:forString:], AppKit.subproj/NSCell.m:1131
2004-07-11 09:49:23.444 CocoaApp[416] Invalid parameter not satisfying: aString != nil
A quel endroit ai-je fait un erreur ?
Merci
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
@implementation EditController
SInt32 currentDive, seekDive;
struct carnet logbk[MAX_NB_DIVE ];
- (IBAction)modifier:(id)sender /* Modification du carnet de plongees */
{
NSString *notes = [[NSString alloc] init];
notes = [notesField stringValue];
currentDive = [plongeeNoField intValue];
logbk[currentDive].coeff = [coeffField intValue];
logbk[currentDive].date = [dateField stringValue];
logbk[currentDive].heure = [heureField stringValue];
logbk[currentDive].lieu = [lieuField stringValue];
logbk[currentDive].site = [siteField stringValue];
/* etc... avec des string */
}
- (IBAction)editOK:(id)sender /* Editer les plongees */
{
seekDive = [seekDiveField intValue];
[plongeeNoField setIntValue:seekDive];
[coeffField setIntValue:logbk[seekDive].coeff];
[dateField setStringValue:logbk[seekDive].date];
[heureField setStringValue:logbk[seekDive].heure];
[lieuField setStringValue:logbk[seekDive].lieu];
[siteField setStringValue:logbk[seekDive].site];
/* etc... */
[notesField setStringValue:notes];
}
@end
Mes variables font partie de la structure suivante :
Fichier "struct.h"
struct carnet
{
SInt32 nbPlongee, coeff;
NSString *date, *heure, *lieu, *site;
/* etc avec les autres string */
};
Le choix d'utiliser une structure mer permettra dans un premier temps de récupérer toutes les plongées que j'ai enregistré avec mon appli sous Carbon (env. 600...).
Voilà
A+
Les NSString, à la différence des foat etc... sont des objets dont tu ne récupères que les pointeurs.
Ta struct ne contiens donc pas vraiment les chaines mais juste des pointeurs.
Si tu veux garantire la validité de tes struct mieux vaux "retenir" les objets comme les NSSTrings
Et bien sur les libérer quand tu te débarasse de la struct
NSString *notes = [[NSString alloc] init];
††notes = [notesField stringValue];
stringValue retourne un NSString, pas besoin d'initialiser notes, en plus tu as une fuite mémoire
dans editOk, notes y sort d'où ? il est initialisé comment?
Pour répondre à mpergand, j'ai supprimé l'initialisation de "notes" => OK
En réalité concernant la sortie de "notes", je l'avais sorti de la structure pour faire des tests. Je l'ai réintégré dans la structure et tout fonctionne normalement. Pour info, "notes" sortait dans un NSTextField.
Concernant l'utilisation de "release" comme me l'a suggéré ClicCool, j'hésite à l'utiliser parce que j'aimerais conserver toutes mes données en mémoire jusqu'au prochain enregistrement ou création d'un nouveau carnet.
Merci à tous
A+
Si c'est ta struct qui garde la référence à ton objet string, ben t'as pas à t'en soucier, tu n'envoies un release à tes strings que quand tu VEUX que la struct soit détruite.
Si tu comptes garder ailleurs une référence à tes string, il faut quand même envoyer un release à la destruction de ta struct.
C'est l'objet qui doit garder une référence à tes strings qui est responsable d'elles et doit envoyer aussi un retain à tes objets strings au moment où elle en reçoit la référence. A son tour cet objet enverra un release lorsqu'il n'en aura plus besoin ...
Les retain-release sont comptabilisés.
Chaque objet qui compte garder une référence à un autre objet doit envoyer un retain à l'objet référencé.
Par la suite il DOIT envoier un release quand il "làche la référence".
Tant que le "retainCount" est superieur à zéro, l'objet reste retenu et ne sera pas désalloué.
Quand TOUS les objets ayant gardé une référence sur cet objet auront envoyé un release, le compte des retains tombe à zéro et l'objet est désalloué car plus personne n'en veut.
A+
ou versa-vice†;D
J'aurais plutôt écrit:
Il faut 1 Release pour pour chaque Retain
En comptant les [[objet alloc] init] qui renvoient un objet avec un retainCount à 1
Contrairement aux constructeurs de commodités qui renvoient un objet "autoreleasé" avec un retainCount à zéro.
Attention, le retain count d'un objet autoreleasé est égal à 1, mais comme l'objet est placé dans l'autorelease pool, il sera automatiquement releasé "plus tard". Quand un objet atteind un retain count de 0, il est désalloué (un dealloc lui est envoyé).
La différence, pour subtile qu'elle paraisse, est notable !
Merci†Arldon
Tant qu'à être précis:
Si cela est vrai pour un objet tout juste renvoyé par un constructeur de commodité, c'est moins certain pour tout objet auquel nous adressons un autorelease.
Si on envoie un autorelease à un objet dont le retainCount est supérieur à 1, il me semble que l'autorelease ne fera que "programmer" l'envoie d'un seul Release, le compteur restera alors supérieur à zéro.
Non ?
lorsque l'on fait:
NSString* maStr=[ monObj stringValue];
obtient-on une copie, si oui est-elle en autorelease†? Est-ce toujours vrai ?
Et si je fais:
// si maStr à un retain count de 1
NSMutableArray* array=[[NSMutableArray]alloc]init];
[array addObject: maStr];†// retain count =2 (+1)
...
[array removeObject: maStr];†// retain count=1 (-1)
et maStr n'est jamais désalloué !
Donc ne pas oublier de faire un autorelease sur maStr !
C'est bien compliqué cette histoire de mémoire en ObjectiveC et je suis convaincu que la plupart des applis ont des bugs à ce niveau ( ex: XCode, voir les messages dans la console)
oui tout à fait, c'est plus logique dans cet ordre
Tout dépend si tu fais :
NSString *maStr=[[NSString alloc] init]; // maStr est ici déclaré et initialisé au niveau local. Ca te fait un retain de 1 : A toi de gérer sa ______________________________libération.
maStr=[monObj stringValue];
Il te faudra faire un [maStr release]; dans ta méthode quand tu n'auras plus besoin de maStr
Si tu fais :
NSString *maStr=[NSString stringWithString:[monObj stringValue]]; // maStr est initialisé en mode temporaire. Tu n'as pas à te soucier de _____________________________________________________sa libération
Si tu fais :
NSString *maStr; // Déclaré en mode global dans ton fichier d'interfaçage (.h)
Ensuite dans ton implémentation (.m), dans la méthode voulue :
maStr=NSString stringWithString:[monObj stringValue retain]; // Tu as besoin de maStr dans une autre méthode donc tu fais un retain
Dans ce cas dans ta méthode -(void)dealloc: il te faudra faire un [maStr release];
....1 release pour 1 retain
Il faut savoir qu'un objet de type "collection" (NSMutableArray, NSMutableDictionary, etc...) gère lui même les objets que l'on stocke dedans. Il s'occupera donc de libérer tout les objets qui sont dedans quand lui-même sera libéré.
Donc dans ton cas, après [array addObject: maStr];
Tu fais un [maStr release];
Et tu retombes sur tes pattes.....
- on ne s'occupe pas de la gestion en mémoire d'un objet qu'on a pas créé soit même.
Donc dans le cas de [monObjet stringValue];, la NSString renvoyée doit être dans l'autorelease pool, si elle a été créé spécialement pour l'occasion. Si elle ne l'est pas, c'est qu'elle est retenue ailleurs. Donc on ne s'occupe pas de faire des release dessus, à moins d'avoir fait excplicitement un retain dessus.
Donc c'est une règle qu'il faut respecter dans le design de ses objets.
Et il y a une autre règle qui dit que si le nom de la classe renvoyée est inclus dans le nom d'une méthode (de classe ou d'instance), on n'a pas à se soucier de le releaser en tant voulu. De nouveau on fait un retain pour s'assurer que l'objet sera bien dispo si on en a besoin de manière plus permanente.
Je ne suis pas sur d'être d'accord avec toi sur ce point.
Avec: NSString *maStr = [[NSString alloc] init]
Tu instancies un objet NSString avec un retainCount à 1 en effet
et tu concerve un pointeur dessus avec maStr.
Mais si après tu fais:
maStr = [monObj stringValue];
Tu change la valeur de ton pointeur (et pas de ton objet string précedemment initialisé) pour qu'il pointe sur la NSString renvoyée par stringValue.
Tu perds du même coup toute référence à la string initialisée dans la ligne précédente.
Tu te retrouves donc avec un objet fantome sans avoir gardé de référence dessus pour le releasé !
le terme 'libérer' est ambigu ici, car il sous-entend que les objets seront désalloués, ce qui n'est vrai que s'il ont un retain de 1.
Oui je suis d'accord avec le fait que tu ne sois pas d'accord
En effet j'ai fait une bourde.....
J'aurais du écrire :
NSString *maStr = NSString alloc] initWithString:[monObj stringValue;
Voilà....
libérer == releaser
Si tu met un objet NSString dans un NSMutableArray par exemple, son retainCount sera obligatoirement à 1 puisqu'un objet avec un retainCount de 0 n'a plus raison d'être et est donc releasé..... Quand tu releaseras ton NSMutableArray, celui-ci s'occupera de releaser tout les objets qu'il comporte.....
NSString *maStr=[monObj stringValue]; // retainCount=1
NSMutableArray *monArray=[[NSMutableArray] alloc] init];
[monArray addObject:maStr]; // retainCount=1 (dans le NSMutableArray)
[maStr release]; // retainCount=0
[monArray release]; // retainCount=0
Je trouve ça très enrichissant et j'espère que c'est instructif aux lecteurs et à ce titre souhaite en voir d'autres voir le jourâ€
A mon sens c'est POSITIF
Merci (dans l'orde d'apparition sur scene :P ) à Favouille84 qui a ouvert ce fil de discution (bienvenu à toi dans le monde Cocoa), à mpergand dont les compétences ne sont plus à démontrer et qui mine de rien argumente en douce pour Java , à Eddy58 qui nous a aidé à souligner plus encore les subtilités de la gestion de la mémoire, à Arldon enfin, dont les avis sont toujours impécablements rigoureux et bien placés.
MERCI Objective-Cocoa et Isostar de nous fournir ce terrain fertle à l'expression et la progression de tousâ€
Voilà, c'était ma minute entousiaste pour la communauté Objective-Cocoa†:-*
Favouille84 tu peux reprendre posséssion de ton fil si tu veux
C'est effectivement très instructif.
Merci à tout le monde. ;-)
Oups je n'avais pas vu celui là... La réponse est oui. Autorelease décrémente le retain count de 1 (de manière décalée). On peut donc sur un même objet combiner les autorelease et release pour peu que le retain count ne passe jamais en dessous 0.
Bon tant qu'à faire un peu de pub. J'avais rédigé à l'époque un tuto sur la gestion de la mémoire, publié ici
En effet, j'ignorais jusqu'ici tes talents didactiques†;D
Ton Tuto est très bien et il†complète admirablement notre discussion
mpergand tu l'as lu j'imagines? tu reste plus favorable au java quand même ? :P ;D
Comme mes tutos sont retativement bien planqués dans la hiérachie de P:O (ils ne sont pas avec la série connue de leurs tutos d'initiation à la programmation (qui est réservée aux traductions de oreilly), je vais ouvrir un nouveau post dans la section ad hoc et y mettre les références des autres.
Qu'en pensez-vous ?
Le sujet sur java et obj-C est là :
http://www.objective-cocoa.org/index.php?topic=98