Pb d'archivage/désarchivage
Radada
Membre
Salut à tous : )
Je suis en train de faire un exo du bouquin du Aaron Hilleglass. Il faut faire une toute petite appli qui permet de dessiner des ovales (avec une NSBezierPath).
Pour les courageux (moi donc ^^), il faut ajouter la sauvegarde et l'annulation.
J'ai une classe GGPanel qui dérive de NSView et qui contient un NSBezierPath et un NSColor. J'ai ajouté les méthode encodeWithDecoder et initWithCoder pour sérialiser mes 2 objets.
Dans ma classe MyDocument, j'ai un pointeur sur le GGPanel et j'ajoute les méthodes qui vont bien( enfin, il doit forcement en manquer...) dans readFromData:ofType:error et dataOfType:error.
Mais si la sauvegarde à l'air de fonctionner (le fichier de sauvergarde contient des données et ça ressemble à ce que j'attends), le chargement des fichiers de sauvegarde ne fonctionne pas du tout. Pourtant je passe bien dans initWithCoder.
Ca doit surement être une bonne grosse erreur de débutant, mais je ne vois vraiment pas ce qui cloche.
Vala, merci : )))
Je suis en train de faire un exo du bouquin du Aaron Hilleglass. Il faut faire une toute petite appli qui permet de dessiner des ovales (avec une NSBezierPath).
Pour les courageux (moi donc ^^), il faut ajouter la sauvegarde et l'annulation.
J'ai une classe GGPanel qui dérive de NSView et qui contient un NSBezierPath et un NSColor. J'ai ajouté les méthode encodeWithDecoder et initWithCoder pour sérialiser mes 2 objets.
Dans ma classe MyDocument, j'ai un pointeur sur le GGPanel et j'ajoute les méthodes qui vont bien( enfin, il doit forcement en manquer...) dans readFromData:ofType:error et dataOfType:error.
Mais si la sauvegarde à l'air de fonctionner (le fichier de sauvergarde contient des données et ça ressemble à ce que j'attends), le chargement des fichiers de sauvegarde ne fonctionne pas du tout. Pourtant je passe bien dans initWithCoder.
Ca doit surement être une bonne grosse erreur de débutant, mais je ne vois vraiment pas ce qui cloche.
Vala, merci : )))
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Tant qu'à y être, si vous avez des pistes (des pistes, pas la solution svp) pour l'annulation... Je sèche un peu : impossible d'utiliser l'undoManager (prepareWithInvokationTarget) puisqu'il n'y a pas moyen (enfin je crois) de soustraire une forme du path lorsqu'on utilise appendBezier..... Et je ne vois pas vraiment comment utiliser addObserver... A moins d'utiliser un path à chaque fois et de l'ajouter à une liste, et on redessine la liste à chaque fois. Mais ça me parait vachement lourd non???
Meeeeeeeeeeeeeerci
Peux-tu montrer ces deux méthodes , en précisant dans quel fichier elles sont implémentées ?
Même demande.
Plantage ? Non apparition de l'ovale ?
Salut et merci pour ton post.
J'ai mis le projet en pièce jointe du premier post, comme ça tu as tout. Dis moi si tu veux quand même que je les remette ici.
Merci
Disons que j'aimerais que tu formules ta question de manière plus précise :
• Qu'est-ce qui ne marche pas exactement, plantage ? non apparition de ce que tu attends ?
• J'ai mis un NSLog() à tel endroit dans le code (citer alors le code) et cela ne m'a pas donné ce que j'attendais,
• J'en ai mis un autre ailleurs, et ...
• ou j'ai lancé le debuggeur et il s'est passé tel chose ...
En gros, la part d'analyse que tu as faite.
Oki doki.
Pour faire la sauvegarde, j'ai donc ajouté les méthodes d'archivage/désarchivage dans le GGPanel. Si je fous un point d'arrêt dans ces méthodes, je passe bien dedans.
Ensuite, j'ai rajouté du code dans MyDocument.m (readFromData... et dataOfType). La encore, si je mets des points d'arrêts, je passe bien dedans.
Lors de la sauvegarde, je génère un fichier avec des données dedans et notamment une couleur, ce qui me fait dire que la sauvegarde marche normalement.
Par contre, le chargement ne fonctionne pas alors que je passe bien dans readDataFromType et initWithCoder. Je me demande si en fait il ne manque pas une intialisation du NSView quelque part dans le initWithCoder... Cela ne fonctionne par car rien ne s'affiche dans le NSView (GGPanel) : il est vierge. Mais je peux de nouveau dessiner dedans...
A mon avis, le pb pourrait venir du
Ca me fait bien passer dans initWithCoder de GGPanel, mais je pense que ça ne me permet pas de construire entièrement un panel, non?
Merci
EDIT : j'ai rajouté l'appel des méthodes des classes mères [super initWithCoder:decoder] et [super encodeWithCoder:encoder] mais ça ne change rien. De toutes façons, je pense qu'elles manquaient....
Mets un NSLog ainsi
Pour construire une NSView, il vaut mieux avoir sa frame.
Oki doki, mais comment la construire avec initWithCoder?
Ou alors il faut que j'archive mon NSBezierPath et NSColor depuis le MyDocument et non depuis le GGPanel? Comme ça je construit un NSView et je lui redonne le path et la couleur? Il vaut mieux procéder ainsi?
Merci
Cela a donné quoi ?
Dans la méthode d'archivage, on encode la frame.
Dans la méthode de désarchivage, on décode ce NSRect et on l'affecte à la frame de la vue.
Il faut ensuite faire un addSubview pour installer la vue sur l'interface.
C'est une autre option, on archive le modèle plutôt qu'archiver la vue. Mais comme c'est un exercice, termine le premier choix et essaie ensuite le second.
Mais, si je décompose ce qui se passe à la création d'un document, il doit se passer ça:
dans readFromData l'outlet panel n'est pas encore initialisé donc = nil
panel = [NSKeyedUnarchiver unarchiveObjectWithData:data];
modifie l'adresse de l'outlet, mais représente une vue attachée dans le vide (pas de fenêtre) .
de plus unarchiveObjectWithData retourne un object en autorelease .
En fait l'outlet panel est initialisé dans windowControllerDidLoadNib.
Donc effectivement tout ça a peu de chance de fonctionner.
Le premier Log est Nil puisque l'objet n'est pas encore chargé et le second pointe sur un GGPanel...
Donc si j'ai bien suivi, dans encodeWithCoder, il faut faire un encodeObject: ForKeyPath: avec un [self frame].
Dans initWithCoder, je désarchive la frame dans un NSRect, on fait un setFrame avec le NSRect, mais comment
faire pour affecter la frame à la vue? Et le addSubView, j'utilise quoi comme objet Vue???
J'avais pas fait gaffe que ma Vue contenait le code du modèle. C'est étrange car c'est ce que Aaron Hilleglass fait dans le chapitre correspondant. Il vaut mieux utiliser un objet model qui contient le path et la couleur et ensuite les faire afficher sur la Vue? Mais dans ce cas là , il n'y a pas besoin de dériver la classe NSView non?
Merci d'avance Philippe pour ton aide
P.S. : idem pour l'annulation, si tu as une ou deux pistes, notamment sur l'affichage des NSBezierPath. Merci
Commençons par la cause de tes ennuis. Dans la méthode -(IBAction)pickColor:(id)sender de AppController mets aussi NSLog(@%@",panel);
Ensuite viendras la question, brutale mais nécessaire : Que fout AppController dans MyDocument.nib ? AppCOntroller est sensé contrôlé l'application et donc l'ensemble de tous les documents. Sa place est dans MainMenu.nib.
Cette méthode pickCOlor a sa place dans la classe MyDocument, représenté dans le xib par FIle's Owner.
Dans les cas simples, on ne construit pas forcément les trois éléments du modèle MVC. Il est clair que la vue doit avoir le NSBezierPath et la couleur pour pouvoir se dessiner, et donc avoir des références à ces valeurs,même si cela constitue le modèle. Mais les contenus de ses variables sont fournis par la couche model, le controller remplissant ce model en indiquant à la vue de se rafraichir en conformité. Ce mécanisme est ici représenté par l'utilisateur dans l'exposé de ce chapitre, via la classe MyDocument, et directement par MyDocument dans le cas d'un archivage du modèle et non de la vue.
Cependant, tu es parti sur l'archivage de la vue, ce n'est pas une sotte idée, car c'est ce que fait un nib. Termine sur cette piste, et aborde ensuite la seconde version.
ainsi que dans MyDocument, dans le load
Effectivement, lors du chargement, ce ne sont pas les mêmes adresses....
Je vais déplacer l'appController (pkoi l'ai-je mis dans MyDocument.xib, c'est un mystère...) et la suite et je te tiens au jus.
Merci de ton aide en tout cas...
En fait, je n'ai absolument pas besoin d'un appController du coup non?
Alors, j'ai donc déplacé le pickColor dans MyDocument.m (qui me sert finalement de modèle non?).
J'ai changé les méthodes suivantes selon ce que tu as préconisé qu dessus :
mais je ne vois vraiment pas quoi faire avec ça :
Dans le GGPanel, je ne vois pas quelle NSView passer et dans MyDocument, je ne vois pas non plus quoi passer... Des idées??? Merci chef ^^
• NSLog(@reading from data) dans la methode - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
• et NSLog(@loading form nib) dans windowControllerDidLoadNib:
Tu vois l'ordre dans lequel c'est appelé. C'est là qu'il faut procéder à l'échange du panel du nib avec le panel désarchivé.
Les problèmes à résoudre:
• Tu as sans doute utilisé le même nom panel pour finalement deux objets différents. Cela va gêner, ne prends pas le même nom pour la vue créée dans readFromData et pour l'iBOutlet.
• Il faut savoir retirer une vue de l'interface [oldView removeFromSuperview];
• Rajouter une vue : [theSuperview addSubview:newView]
• et un autre auquel tu peux répondre
j'ai ajouté une variable à MyDocument.h
et voilà ça marche !
Mais c'est comme même n'importe quoi ;D
La ligne [self initWithFrame:rect]; n'est pas à mettre, on n'initialise pas deux fois !
lol oki doki.
J'ai réussi à afficher de que j'avais dans le fichier de sauvergade mais j'ai un planton maitenant avec une adresse à 0x00, il faut que je creuse...
Je te tiens au jus. Merci de ton aide en tout cas
Etrange comportement. Je recharge bien toutes les données, mais tant que je ne cliques pas dans la frame, elle ne se rafraichit pas...
Pour info, j'ai pondu le code d'après tes indications, je ne l'ai pas pompé de mpergand
Des idées par hasard svp?
Autre truc étrange, si je fais un [panel release] juste après le removeFromSuperView, ça me fait planter l'appli... Une idée également? Peut être parce qu'un OUTLET n'est jamais nil ou ne peut pas l'être?
Et enfin dernièrement : est-ce que je peux implémenter un mécanisme d'annulation tel quel? Je ne vois pas vraiment comment sachant qu'il n'existe pas de méthode qui supprime une forme d'un NSBezierPath non?
Merci d'avance de tes réponses.
C'est normal le panel est releasé par removeFromSuperView.
Ce qu'il faut faire c'est:
[loadedPanel release];
car il est retenu 2 fois, une fois par [[NSKeyedUnarchiver unarchiveObjectWithData:data] retain]
et une fois lors de l'ajout par addSubview.
Il ne faut pas croire que faire [panel release] avec par la suite un dealloc fait que panel=nil. Le dealloc désalloue la zone pointée par panel, la rendant réutilisable pour d'autres instances, mais panel garde sa référence à cette zone mémoire.
Expérience:
L'exécution montre que string n'est pas nil, et que le contenu de la mémoire pointée sur le tas a été remplacée ici par la chaà®ne utilitaire @qui vaut encore : (évidemment le contenu de l'adresse est indéterminée, on ne peut pas savoir ce qu'il y aura à l'exécution)
J'avais hésité à faire ça, mais j'ai encore trop de réflexes C++. J'avais peur que ça vide la zone mémoire complètement : )
Merci