Encodage classe mère multiple

MulotMulot Membre
août 2007 modifié dans API AppKit #1
Me revoilà , pour de nouvelles aventures chocolatées !

Voilà  j'ai mon application qui est basée sur le modèle NSDocument.

Mon contrôleur (myDocument) possède une table où je stocke les "AccountOwner" qui sont crées dans mon applis. Chaque AccountOwner possède un tableau de compte, et chaque compte possède un tableau d'opérations.

Je ne pense pas que de ce côté là  il y ai un problème, j'ai ajouté le protocole et surchargé les méthodes du protocole NSCoding.

Mon contrôleur contient donc une table mutable contenant des AccountOwners, possède deux pointeurs de type Account et AccountOwner vers des objets qui sont contenus dans la table précédente. Ils pointent sur l'utilisateur et le compte courant dans l'appli, je n'ai donc pas fait d'alloc pour ces simples pointeurs.

De plus, j'ai aussi une floppée de booléens dans mon controller, en plus des outlets (mais ceux si n'ont rien a voir avec l'archivage).

Je pensais donc bien faire en rendant ma classe myDocument compatible avec le protocole NSCoding.

J'ai donc ajouté une méthode encodeWithCoder: (NSCoder*) aCoder;

<br /><br />- (void) encodeWithCoder: (NSCoder*) aCoder {<br /><br />//encoding the dictionnary<br />[aCoder encodeObject: userDictionary];<br /><br />//encoding pointers<br />[aCoder encodeObject: currentUser];<br />[aCoder encodeObject: currentAccount];<br /><br />//encoding booleans<br />[aCoder encodeValueOfObjCType:@encode(BOOL) at &amp;operationDesignationSort];<br />...<br />...<br /><br />}<br />


Ensuite dans ma méthode dataRepresentationOfType:

j'ai mis:
<br />...<br />//ici self est myDocument<br />return [NSArchiver archivedDataWithRootObject: self];<br /><br />


Ma méthode de décodage pour myDocument est comme ceci:

<br />...<br /><br />if (self = [super init] ){<br />//setUserDictionary est un setteur &quot;normal&quot; retain, release puis pointe sur le param<br />[self setUserDictionary: [aCoder decodeObject]];<br /><br />currentUser = [aCoder decodeObject];<br />currentAccount = [aCoder decodeObject];<br /><br />[aCoder decodeValueOfObjCType: @encode(BOOL) at &amp;operationDesignationSort];<br />...<br />}<br /><br />return self;<br />]<br /><br />


J'ai mis des petis NSLog dans mes méthode encode, et je remarque que lorsque je sauvegarde mon document, il encode ce qu'il faut correctement, mais le fait plusieurs fois d'affilée ! Je ne vois pourtant pas de "boucle".

Un autre point, est-ce que mes booleens et mes pointeurs sotn "encodés" et "décodés" comme il faut ? C'est à  dire que l'objet pointé par currentUser est déjà  archivé, on notera juste sa référence dans la table de stockage, non ? Et ma méthode de décodage refera bien pointer currentUser sur celui encodé auparavant ?

Un autre point, pour la méthode loadRepresentationOfType, j'ai quelques doutes avec ma démarche d'encoder le fichier de type NSDocument.

<br />- (BOOL) loadRepresentationOfType: (NSData*)dat ofType:(NSString*) type {<br /><br />//release userDictionary<br />[userDictionary release];<br /><br />userDictionary = [[NSUnarchiver unarchiveObjectWithData:data]retain];<br />[self updateUI];<br />return YES;<br /><br />}<br />


Bon là  j'ai de gros doutes, lorsque j'ai testé d'ouvrir un fichier enregistré rien ne se passe. Mais je ne sais pas si ce que j'ai fait est bon, puisque d'un coté j'encode tout mon myDocument, et là  je ne touche qu'à  la table. SI vous pouviez m'éclairer quelque peu !

«1

Réponses

  • Philippe49Philippe49 Membre
    août 2007 modifié #2
    as-tu parcouru :

    file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/Archiving/index.html
  • fouffouf Membre
    23:55 modifié #3
    Je te réponds rapidos (manque un peu de temps ...)
    Lorsque tu charges un doc, loadData.. est appelé avant awakeFromNib donc les actions que tu feras sur des outlets dans le setupUI devront être refaites dans le awakeFromNib ...
  • Philippe49Philippe49 Membre
    23:55 modifié #4
    Voici un exemple qui marche (contentArray est un tableau de MyDocument contenant des instances de type Node)

    Classe MyDocument

    - (NSData *)dataRepresentationOfType:(NSString *)aType
    {
    NSData *data;
    data = [NSKeyedArchiver archivedDataWithRootObject:contentArray];
    return data;
    }

    - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
    {
    contentArray = [[NSKeyedUnarchiver unarchiveObjectWithData:data] retain];
    return YES;
    }


    Classe Node
    #pragma mark ++ Coder Methods  ++

    -(void) encodeWithCoder:(NSCoder *)coder
    {
    [coder encodeObject:name forKey:@NodeName];
    [coder encodeObject:children forKey:@NodeChildren];
    [coder encodeObject:elements forKey:@NodeElements];
    }

    -(id) initWithCoder:(NSCoder *)  coder
    {
    self=[super init];
    if(self){
    name=[[coder decodeObjectForKey:@NodeName] retain];
    children=[[coder decodeObjectForKey:@NodeChildren] retain];
    elements=[[coder decodeObjectForKey:@NodeElements] retain];
    }
    return self;
    }


    J'ai fait l'impasse sur le test allowsKeyCoding
    Quand le codage n'est pas en KeyedArchiver, on doit respecter l'ordre du codage dans le décodage.
  • MulotMulot Membre
    août 2007 modifié #5
    Pour l'instant j'essaie de comprendre pourquoi lors de ma sauvegarde mon cycle d'encode se fait 3 fois de suite.

    Je n'utilise pas de KeyedArchiver/unarchvier parce que je n'en vois pas l'intérêt dans mon cas, et je respecte bien l'ordre d'encodage lors du décodage.

    Mon soucis serait plus sur le fait que je fais implémenter le protocol NSCoding à  ma classe NSDocument qui est le contrôleur.

    J'ai suivit le même exemple que dans "Cocoa par la Pratique", sauf que dans l'exemple d'archivage, il n'y a pas de controler à  encoder, le contrôleur contient juste un tableau d'objets implémentant le protocole NSCoding.

    Dans mon cas j'ai une table aussi à  encoder dans le contrôleur, mais j'ai aussi d'autres objets et variables. Faut-il donc que je rende mon contrôleur compatible avec le protocole NSCoding ?

    Si oui l'objet racine à  encoder doit être lui même non ?

    Pourquoi j'obtient un "triple" encodage alors que mon arbre d'archivage est simple ? (Contrôleur possède une table d'objet AccountOwner, chaque AccountOwner a un tableau de Compte, chaque Compte a un tableau d'opération).

    Pourtant lors d'un "cycle" d'encodage, tout se fait parfaitement bien à  premiere vue.

    Par exemple, suite à  l'appel de la méthode dataRepresentationOfType, je passe bien dans la méthode encodeWithCoder de mon contrôleur, il encode la table, mon utilisateur dans la table est bien encodé, ensuite le compte de celui ci est bien encodé, et enfin l'opération dans le compte de l'utilisateur ets encodée. Ensuite mes pointeurs sont encodés, mes booléens sont encodés, mais ce cycle est refait plusieurs fois ensuite, un problème avec mes pointeurs ? Dois-je les encoder avec l'option encodeConditionalObject ? (puisque ces objets sont fatalements présent dans la table).

    Merci pour vos réponses, l'archivage me faisait un peu peur, et je ne pense pas arriver à  tout comprendre pour l'instant sur ce point non plus.
  • Philippe49Philippe49 Membre
    23:55 modifié #6
    dans 1187631794:

    Pour l'instant j'essaie de comprendre pourquoi lors de ma sauvegarde mon cycle d'encode se fait 3 fois de suite.


    Au début tu encodes donc un tableau . Quelle est la nature de chaque objet de ton tableau, et respectent-ils NScoding ?
  • Philippe49Philippe49 Membre
    23:55 modifié #7
    dans 1187631794:


    Mon soucis serait plus sur le fait que je fais implémenter le protocol NSCogin à  ma classe NSDocument qui est le contrôleur.


    Comme mon exemple, il semble plus logique d'encoder la table, chaque objet de cette table devant respecter le protocole NSCoding
  • MulotMulot Membre
    23:55 modifié #8
    Oui je voudrais bien juste encoder ma table, mais le soucis c'est que j'ai d'autres objets dans le contrôleur à  encoder.

    Je ne peux donc pas me permettre de faire :

    <br />NSData *data;<br />&nbsp;  data = [NSKeyedArchiver archivedDataWithRootObject:contentArray];<br />&nbsp;  return data;<br />
    


    Les objets contenus dans ma table implémentent tous le protocole NSCoding, ils s'encodent bien, chaque objet encodé appelle bien l'objet "enfant" qu'il faut Mon controlle encode ma table, ma table encode mon User, mon USer encode son compte, son compte encode son opération.

    Le seul soucis est que ce cycle est fait 2 fois. J'ai mis un NSLog au début de ma méthode encodeWithCoder de myDocument.m, et ce NSLog est bien appellée deux fois. Etrange, n'y a til pas un appel "automatique" liée au fait que ça soit un document ou que je tente d'archiver self ?

    J'obtient le même résultat si je n'encode pas mes pointeurs, ou si je les encode en conditional.

  • Philippe49Philippe49 Membre
    août 2007 modifié #9
    dans 1187638428:


    Le seul soucis est que ce cycle est fait 2 fois.



    Il serait logique que lors du [NSArchiver archivedDataWithRootObject:self] une première demande de codage soit faite (encodeRootObject:), comme l'indique la  doc 
    Root Object
    An object graph is not necessarily a simple tree structure. Two objects can contain references to each other, for example, creating a cycle. If a coder follows every link and blindly encodes each object it encounters, this circular reference will generate an infinite loop in the coder. Also, a single object can be referenced by several other objects. The coder must be able to recognize and handle multiple and circular references so that it does not encode more than one copy of each object, but still regenerate all the references when decoding.

    To solve this problem, NSCoder introduces the concept of a root object. The root object is the starting point of an object graph. To encode an object graph, you invoke the NSCoder method encodeRootObject:, passing in the first object to encode. Every object encoded within the context of this invocation is tracked. If the coder is asked to encode an object more than once, the coder encodes a reference to the first encoding instead of encoding the object again.

    NSCoder does not implement support for root objects; NSCoder's implementation of encodeRootObject: simply encodes the object by invoking encodeObject:. It is the responsibility of its concrete subclasses to keep track of multiple references to objects, thus preserving the structure of any object graphs.
  • MulotMulot Membre
    23:55 modifié #10
    dans 1187643396:

    NSCoder does not implement support for root objects; NSCoder's implementation of encodeRootObject: simply encodes the object by invoking encodeObject:. It is the responsibility of its concrete subclasses to keep track of multiple references to objects, thus preserving the structure of any object graphs.[/i]


    Donc si j'ai compris, lorsque je fais:

    <br />return [NSArchiver archivedDataWithRootObject: self];<br />
    


    Cela veut dire que  encodeObject est envoyé à  self ? Donc en théorie, vu que ma classe implémente NSCoding, encodeObject devrait bien marcher, et donc encodeObject devrait appeler ou utiliser ma méthode encodeWithCoder que j'ai surchargé non ?

    Je ne vois pas pourquoi il réencode le tout une deuxième fois à  vrai dire, c'est quand même pas quelque chose de normal tout de même ?

    De plus mon diagramme de classe est un arbre.

                                                    myDocument
                              _______ ______ _____|_____________________
                              |                      |              |                    |
                              |                      |              |                    |
                          userDictionary  currentUser  currentAccount  boolean ...(...)
                                  |                    |                    |
                            AccountOwner  <---/                      |
                                    |                                        |
                                Account  <
    /
                                    |
                              Operation


    Desole pour cet immondice mais mon mac est à  l'étage ! Donc je ne remarque pas de cycle ou de référence croisées dans mon code. Si quelqu'un peut m'expliquer un peu plus en détails le principe de fonctionnement de NSArchiver, et qui se passe réellement de l'encodage du rootObject.

    Merci !
  • Philippe49Philippe49 Membre
    août 2007 modifié #11
    Voilà  un petit essai simplifié. La méthode readFromData: ofType: error: (ou load..) appelle initWithCoder (via unarchiveObject) pour créer une nouvelle instance du controleur, qui n'est pas self ..
    Ce qui fait que dans readFromData, on est amené à  transférer les données.


    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)aType error:(NSError**) addError
    {
    NSLog(@reading from data);
    id object=[[NSKeyedUnarchiver unarchiveObjectWithData:data] retain];
    [self setValue:[object valueForKey:@number] forKey:@number];
    [self setValue:[object valueForKey:@string] forKey:@string];
    NSLog(@number:%d string:%@",[number intValue],string);
    return YES;
    }


    [Fichier joint supprimé par l'administrateur]
  • MulotMulot Membre
    23:55 modifié #12
    dans 1187650602:

    Voilà  un petit essai simplifié. La méthode readFromData: ofType: error: (ou load..) appelle initWithCoder (via unarchiveObject) pour créer une nouvelle instance du controleur, qui n'est pas self ..


    Donc en fait pour le désarchivage, je dois déclarer un objet de type myDocument, qui sera retournée par le NSUnarchiver, et je prend les valeurs de cet objet, que je réaffecte à  self donc ?

    Par contre j'ai toujours mon soucis de double encodage, j'aimerai bien régler ce soucis avant de commencer le désarchivage, et je ne vois pas d'où ça pourrait venir.

    Quelqu'un à  une piste à  me donner ?
  • Philippe49Philippe49 Membre
    août 2007 modifié #13
    Principes généraux

    Objective-C recommande en général de séparer
    - le modéle (les données)
    - le controleur
    - la vue
    c'est ce qu'ils appellent le principe Model-View-Controller

    Si il est encore temps dans ton appli de le faire, le controleur possèderait un champ MyModel * model qui serait l'unique objet archivé et désarchivé en rootObject. C'est donc cettte classe qui serait compatible avec le protocole NSCoding, et non le controleur.

    Autrement
    je n'ai pas d'autres solutions que celles de recopier les données.

    double encodage
    à  l'essai du code que je t'ai envoyé, on n'a pas cela, mais peut-être l'explication y est quand même :
    Lors du désarchivage, la méthode init de la sous-classe est appelée : il faut bien que self existe avant l'appel de la méthode readFromData
  • MulotMulot Membre
    23:55 modifié #14
    J'ai fait mon appli en essayant de la faire le plus possible suivant le modèle MVC. Mais là  j'ai un tas d'outlets et d'attributs dans mon controller.

    Pour faire un controller avec un seul objet de type myModel, il faudrait que je "déplace" tout mes attributs dans cette classe myModel, avec les méthodes privées, les méthodes d'IB, ainsi que les outlets et l'implémentation des délégates et autres dataSource donc ?

    Dans myDocument, il aura donc une méthode init du genre

    <br />- (id) init {<br /><br />if (self = [super init]){<br /><br />&nbsp; myModel = [[MCModel alloc] init];<br />&nbsp; // le init de myModel contiendrait donc mon ancien init, qu&#39;en est il du notification center<br />&nbsp; //que j&#39;ai jaouté à  ma classe myDocument au départ ? <br />&nbsp; ...<br /><br />}<br />return self;<br />}<br />
    


    Il faut donc je laisse les méthodes loadDataRepresentionOfType, readDataRepresentionOfType, et toutes celles propres à  myDocument en fait dans myDocument.

    D'un point de vue .NIB, il va falloir que j'instancie une classe de type MCModel dans le .NIB, et qu'elle soit l'unique outlet du FileOwner (myDocument) ?

    Merci pour ta patience Philippe, je vais tacher de faire cette modification cette après midi, je vous tiendrez au courant.
  • Philippe49Philippe49 Membre
    août 2007 modifié #15
    dans 1187692838:

    J'ai fait mon appli en essayant de la faire le plus possible suivant le modèle MVC. Mais là  j'ai un tas d'outlets et d'attributs dans mon controller.

    Pour faire un controller avec un seul objet de type myModel, il faudrait que je "déplace" tout mes attributs dans cette classe myModel, avec les méthodes privées, les méthodes d'IB, ainsi que les outlets et l'implémentation des délégates et autres dataSource donc ?

    à  priori les delegate et data-source, les IBAction sont des méthodes de contrôle, elles doivent rester dans le controleur:
    -Le modèle reçoit les données manipulées (=le fondamental pour reconstituer ton document) , et les méthodes comme encodeWithCoder: , initWithCoder:.
    -Le contrôleur s'occupe des méthodes de gestion sur la vue, répercute les transformations du modèle décidées par l'utilisateur, et déclare les utilitaires nécessaires.


    dans 1187692838:

    Dans myDocument, il aura donc une méthode init du genre

    <br />- (id) init {<br /><br />if (self = [super init]){<br /><br />  myModel = [[MCModel alloc] init];<br />  // le init de myModel contiendrait donc mon ancien init, qu&#39;en est il du notification center<br />  //que j&#39;ai jaouté à  ma classe myDocument au départ ? <br />  ...<br /><br />}<br />return self;<br />}<br />
    



    attention aux initiations multiples lors du désarchivage.
    Les notifications se traitent à  priori dans le contrôleur.

    dans 1187692838:

    Il faut donc je laisse les méthodes loadDataRepresentionOfType, readDataRepresentionOfType, et toutes celles propres à  myDocument en fait dans myDocument.

    oui, et également les IBAction, les méthodes de mise à  jour de l'interface graphique

    dans 1187692838:

    D'un point de vue .NIB, il va falloir que j'instancie une classe de type MCModel dans le .NIB, et qu'elle soit l'unique outlet du FileOwner (myDocument) ?

    non, le modèle n'a pas besoin d'être un outlet
  • MulotMulot Membre
    23:55 modifié #16
    Aie, si il faut que les IBAction et les delegate et cie restent dans myDocument, tout de suite ça ne me motive pas pour le faire, ça risque d'être trop le tatouin, niveau accès, ça va être un bordel monstre à  modifier, je vais donc essayer de trouver une solution subsidiaire au soucis de double encodage, et prendre note de bien séparer le contrôleur et ses méthodes et les autres méthodes "privées" dans un seul objet de type modèle.

    double encodage
    à  l'essai du code que je t'ai envoyé, on n'a pas cela, mais peut-être l'explication y est quand même :
    Lorsque initWithCoder appelle [super init] on voit que c'est la méthode init de la sous-classe qui est appelée.


    Je pense que tu as voulu dire la méthode init de la super-classe plutôt non ?

    Par contre je viens d'y penser à  l'instant, je me rappelle dans "Cocoa par la Pratique", que l'auteur précise que si on cherche à  faire implémenter le protocole NSCoding à  une classe, il y a deux possibilitées:

    - soit la super-classe implémente le protocole NSCoding, et dans ce cas dans le initWithCoder il faut faire:

    <br />-(id) initWithCoder: (NSCoder*) aCoder {<br />if (self = [super initWithCoder:aCoder]){<br /><br />...<br />}<br />}<br />
    


    - soit la super classe ne l'implémente pas et dans cas il faut simplement faire [super init].

    Par contre je ne vois pas trop le lien entre la méthode initWithCoder et mon soucis que j'ai à  l'encodage, pour l'instant je n'ai pas essayé le désarchivage, je préfère que mon archivage marche correctement avant de m'y lancer, pour éviter d'avoir des problèmes avec un archivage foireux.
  • Philippe49Philippe49 Membre
    23:55 modifié #17
    dans 1187697233:

    Je pense que tu as voulu dire la méthode init de la super-classe plutôt non ?

    Non pas exactement, j'ai rectifié dans le post précédent.
  • Philippe49Philippe49 Membre
    23:55 modifié #18
    dans 1187697233:

    Par contre je ne vois pas trop le lien entre la méthode initWithCoder et mon soucis que j'ai à  l'encodage, pour l'instant je n'ai pas essayé le désarchivage, je préfère que mon archivage marche correctement avant de m'y lancer, pour éviter d'avoir des problèmes avec un archivage foireux.


    je pense qu'il y a un mic-mac dans ton code.

    voilà  le log sur l'essai que je t'ai proposé :

    [Session started at 2007-08-21 14:56:20 +0200.]
    -- Là  je précise les valeurs d'un document Untitled--
    2007-08-21 14:56:20.816 essaiCoderMultiple[2420] initializing
    2007-08-21 14:56:21.398 essaiCoderMultiple[2420] controller did load nib, number=0 string=(null)
    2007-08-21 14:56:39.261 essaiCoderMultiple[2420] new string is : toto
    2007-08-21 14:56:42.867 essaiCoderMultiple[2420] new number is : 123

    -- Là  j'enregistre mon document, et le ferme --
    2007-08-21 14:56:48.793 essaiCoderMultiple[2420] encoding with coder

    -- Là , je le rouvre, et on voit bien qu'une initialisation d'instance a lieu avant readFromData --
    -- comment cela se pourrait-il autrement puisque readFromData:  est une méthode d'instance --
    2007-08-21 14:56:57.875 essaiCoderMultiple[2420] initializing
    2007-08-21 14:56:57.876 essaiCoderMultiple[2420] reading from data
    2007-08-21 14:56:57.876 essaiCoderMultiple[2420] init with coder
    2007-08-21 14:56:57.876 essaiCoderMultiple[2420] number in initWithCoder : 123
    2007-08-21 14:56:57.876 essaiCoderMultiple[2420] toto
    2007-08-21 14:56:57.876 essaiCoderMultiple[2420] number:123 string:toto
    2007-08-21 14:56:57.878 essaiCoderMultiple[2420] controller did load nib, number=123 string=toto

  • Eddy58Eddy58 Membre
    23:55 modifié #19
    dans 1187697233:

    Aie, si il faut que les IBAction et les delegate et cie restent dans myDocument, tout de suite ça ne me motive pas pour le faire, ça risque d'être trop le tatouin, niveau accès, ça va être un bordel monstre à  modifier, je vais donc essayer de trouver une solution subsidiaire au soucis de double encodage, et prendre note de bien séparer le contrôleur et ses méthodes et les autres méthodes "privées" dans un seul objet de type modèle.

    Je ne peux qu'appuyer Philippe49, qui te conseille de repartir sur des bases saines. Fais une classe modèle propre, répondant au protocole NSCoding, dans laquelle tu implémentes tout tes setters/getters et méthode d'archivage, ensuite tu y verras beaucoup plus clair, et tu y gagneras du temps pour la suite du projet. :o
  • MulotMulot Membre
    23:55 modifié #20
    Purée génial, j'avais fais un beau post et il a pas été posté ! :/

    Bon je tente de reprendre ce que je voulais dire.

    Le soucis c'est que mon appli est quasiment finie dans sa première version, et qu'il ne manque plus que l'archivage et quelque retouches pour être opérationnelle (bien que je vais ajouter pas mal de choses ensuite).

    Si il faut simplement virer certaines méthodes de mon contrôleur, ajouter un attribut myModel à  mon contrôleur et y mettre les ex méthodes privées de mon ex contrôleur, instancier ma classe myModel dans myDocument.NIB, remettre les outlets et IBactions à  jour si les outlets vont dans l'objet myModel, et remplacer des self par myModel dans mon contrôleur, ça devrait aller, seulement j'ai peur de ne pas avoir bien codé et de voir réaparaitre des soucis.

    Autrement, si je me met à  faire cette "migration", il faut que mon contrôleur contienne :

    - les delegates,
    - les data sources,
    - l'ajout d'objets (qui seront contenus dans l'attribut myModel) au centre de notification,
    - méthodes d'actions,
    - méthodes créés automatiquement pour un NSDocument,
    - méthode init,
    - les outlets ?

    Pour ce qui est de ma méthode pour updater l'interface Philippe, ce n'est pas une méthode d'action, elle doit donc se trouver dans muModel non ? De plus je l'appelle par exemple lorsque la selection de ma tableView change, ou quand une combo box est modifiée, ou suite à  un appel ou fin d'une feuille.

    Pour les outlets, si ils sont dans mon contrôleur je devrais les mettre en scope protected puisque je me sert des outlets dans des méthodes qui seront présentes dans myModel.

    Donc dans myModel, j'aurais :

    - méthodes privées qui seront appelées soit par d'autre méthodes privées de myObject, ou par des méthodes d'action dans le contrôleur,
    - méthode d'archivage de l'objet myModel qui devra être capable d'implémenter le protocole NSCoding.

    Est-ce que ceci pourrait être quelque chose de bon à  faire ? Je ne sais pas trop vu l'état actuel de l'appli et la façon dont j'ai (mal) codé ce projet, si ça sera faisable sans une boite d'aspirine.

    D'ailleurs pour les personnes qui le souhaitent (et qui ont le temps et l'envie), je met mon projet actuel disponible, pour que vous puissez voir l'étendue des dégâts ! Faites pas attention à  mes commentaires souvent stupides ou très mal formulés en anglais approximatif !

    Attention, je risque de vous choquer dans mon utilisation de Cocoa ou dans le "style", j'ai utilisé des méthodes qui ne sont peut être pas les bonnes, alors qu'une autre aurait pu suffir (je pense notament à  la gestion des comboBox en mode dataSource, pour ceux qui auront le courage de regarder).

    Bien entendu je suis ouvert à  toute critique constructive pouvant m'aider à  mieux comprendre le model pattern MVC, et la philosophie Cocoa, et profiter de votre expérience.






    [Fichier joint supprimé par l'administrateur]
  • Philippe49Philippe49 Membre
    août 2007 modifié #21
    dans 1187714556:

    remplacer des self par myModel dans mon contrôleur, ça devrait aller,


    c'est un peu ça, peut-être même seulement ça ...
    Mais c'est vrai que vu l'état d'avancement de ton projet, cela risque d'avoir pas mal d'effets de bord ..
    A toi de voir si ton projet est un essai à  mener à  bien, ou si tu veux en faire un produit que tu pourras ensuite mettre à  jour ... 

    dans 1187714556:

    Pour ce qui est de ma méthode pour updater l'interface Philippe, ce n'est pas une méthode d'action, elle doit donc se trouver dans myModel


    non, pas du tout
    En fait myModel, c'est comme ce que tu as fait avec tes classes Account et AccountOwner : des stockeurs de données, relativement inertes.
    Tu rajoutes un étage à  ton système de données pour simplifier l'architecture de ton programme.



    Ta liste de booléens est impressionnante, et peut être réduite à  un seul int : connais-tu la méthode des drapeaux (flags) ?
      
  • MulotMulot Membre
    23:55 modifié #22
    dans 1187720005:

    A toi de voir si ton projet est un essai à  mener à  bien, ou si tu veux en faire un produit que tu pourras ensuite mettre à  jour ... 


    La version actuelle sera aussi chiante à  faire évoluer selon toi ?

    J'ai pensé à  rejouter pas mal de choses à  ce projet, comme par exemple une exportation d'une opération vers iCal, pour une opération importante, via AppleScript (par exemple l'achat d'un nouvel iMac 24"!).
    Aussi ajouter un "outil" statistique, comme par exemple différents diagrammes représentants les credits / Débits pour une durée donnée ou tels ou tels type d'opérations.
    L'ajout de catégories pour chaque opération, ensuite statistiques pour chaque catégorie, ce genre de choses.


    non, pas du tout
    En fait myModel, c'est comme ce que tu as fait avec tes classes Account et AccountOwner : des stockeurs de données, relativement inertes.
    Tu rajoutes un étage à  ton système de données pour simplifier l'architecture de ton programme.


    Donc dans mon cas précis avec le code actuel que j'ai, myModel contiendrait en fait un NSMutableDictionary contenant des AccountOwner, deux pointeurs de type Account et AccountOwner, ainsi que quelques autres variables (int et/ou booléens) ?

    Si tu as regardé en vitesse mon code, est-ce que mes méthodes que j'appelle "méthodes privée" (enfin celles qui ne sont pas des IBAction), seront à  leur place dans myModel? Comme par exemple celles du genre: createUser:, editAccount: , raiseCreateAccountSheet: et les autres ? Parce que ces méthodes sont en quelque sorte dépendantes de l'UI et des outlets qui sont dans la classe parente (le contrôleur).

    J'ai peur de ne pas arriver séparer convenablement ce qu'il faut dans le contrôleur et myModel: par exemple pour créer un utilisateur, il faut que le nom et prénom ne soit déjà  pas dans la table, sinon j'avertit l'utilisateur et je reset les champs de saisie, ou si je supprime un utilisateur, il faut que je remette à  jour le string dans ma combobox adéquate, bref un tas de choses que je peux uniquement faire si je connais les éléments de l'UI et les outlets, je vois mal comment faire ça de façon générique (peut être avec des méthodes avec tout ce qui est nécessaire en paramètre ?).

    Autres petite question, auriez vous un projet relativement simple ou un tutoriel où je puisse voir la mise en place d'un objet myModel dans un contrôleur ? Est-ce quasiment une obligation de passer par ce genre de modèle ?


    Ta liste de booléens est impressionnante, et peut être réduite à  un seul int : connais-tu la méthode des drapeaux (flags) ?


    Ouai, je me trimballe pas mal de booléens, rien que 6 pour le tri de mon tableau, vu que je n'utilise pas les bindings. De plus maintenant que je lance un tri quand on clique sur une entête de colonne, je peux plus resizer les colonnes !

    Remplacer les booléens par un int, j'y ai pensé, mais il n'y pas longtemps à  vrai dire, seulement lorsque j'ai ajouté un NSPopUpButton pour affichier uniquement les opérations de tel ou tel type (que je n'ai pas encore implémenté d'ailleurs, c'est juste joli pour l'instant).

    Je ne connais pas la méthode des flags, mais il me semble avoir vu un article sur le forum là  dessus, merci du tuyau !
  • Philippe49Philippe49 Membre
    août 2007 modifié #23
    dans 1187723376:

    La version actuelle sera aussi chiante à  faire évoluer selon toi ?

    Si tu as regardé en vitesse mon code, est-ce que mes méthodes que j'appelle "méthodes privée" (enfin celles qui ne sont pas des IBAction), seront à  leur place dans myModel? Comme par exemple celles du genre: createUser:, editAccount: , raiseCreateAccountSheet: et les autres ? Parce que ces méthodes sont en quelque sorte dépendantes de l'UI et des outlets qui sont dans la classe parente (le contrôleur).

    Il faudrait plus qu'une lecture superficielle pour te répondre.

    dans 1187723376:

    la mise en place d'un objet myModel dans un contrôleur ? Est-ce quasiment une obligation de passer par ce genre de modèle ?

    Non ce n'est pas une obligation de détacher physiquement (=créer une classe) le modèle du controleur.
    C'est un peu comme l'utilisation des variables globales en C : au bout d'un certain de niveau de complexité, cela nuit, alors qu'au début on a l'impression que cela simplifie les choses. C'est un peu ce qui s'est passé avec ton problème d'archivage.
  • MulotMulot Membre
    23:55 modifié #24
    Merci beaucoup pour ces éléments de réponse.

    Je vais tenter de mettre ne application ma classe MCModel, en dupliquant mon projet (pas envie de ruiner l'ancien tout de même), si je galère de trop, je me pencherai sur mon ex-code, à  savoir plus exactement le pourquoi du comment du double archivage.

    En attendant je laisse le topic en suspend, j'aurais sûrements d'autres questions sur l'archivage et le désarchivage.
  • Philippe49Philippe49 Membre
    23:55 modifié #25
    J'ai parcouru un peu tes méthodes.
    Les booléens ne sont-ils pas des choix de l'utilisateur à  un moment donné? En ce cas, ils n'ont pas besoin d'être archivés dans le document enregistré, cela simplifie considérablement les fonctions d'encodage-décodage
    Si ce sont des choix importants cela se fait traditionnellement dans les préférences.

    Dans la méthode initWithCoder, le return self devrait être extérieur au if(}

    Bon courage
  • MulotMulot Membre
    23:55 modifié #26
    J'ai un groupe de 3 booléens, qui correspondent en fait à  l'état d'un popUpButton, vu que j'ai ensuite 3 NSButton normaux, (créer, éditer, et supprimer).

    Donc c'est vrai qu'il n'est pas nécessaire de les encoder, il suffira que je selectionne par default un  item du NSPopUpButton lors du chargement du .NIB, merci de la remarque !
  • MulotMulot Membre
    23:55 modifié #27
    Je me permet de reposter, j'ai cherché à  comprendre pourquoi il y avait cet encodage qui se fait doublement, et je n'ai pas trouvé. L'encodage se fait dans le bon ordre, il appelle bien la méthode des objets "fils", mais je ne vois vraiment pas pourquoi il y a ce double encodage.

    J'ai pourtant ma méthode d'archivage qui correspond à  ceci dans mon controller:

    <br />- (NSData *) dataRepresentationOfType:(NSString *)aType<br />{<br /><br />&nbsp; &nbsp; [tableView deselectAll:nil];<br />&nbsp; &nbsp; NSLog(@&quot;La on sauvegarde&quot;);<br />&nbsp; &nbsp; return [NSArchiver archivedDataWithRootObject: self];<br />}<br />
    


    Mon controlleur dispose d'une méthode encodeWithCoder: comme suit:

    <br />- (void) encodeWithCoder: (NSCoder*) aCoder {<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [aCoder encodeObject:userDictionary];<br /><br />&nbsp; &nbsp; [aCoder encodeConditionalObject:currentAccount];<br /><br />&nbsp; &nbsp; [aCoder encodeConditionalObject:currentUser];<br /><br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationDesignationSort];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationAmountSort];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationDateSort];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationValueDateSort];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationTypeSort];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationFlowSort];<br /><br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;accountSelected];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;userSelected];<br />&nbsp; &nbsp; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &amp;operationSelected];<br />&nbsp; &nbsp; &nbsp; <br />}<br />
    


    userDictionary est un simple NSMutableDictionary, contenant des AccountOwner (conforme au protocole NSCoding).

    Cette méthode fonctionne convenablement puisque les méthodes encodeWithCoder: de AccountOwner, puis Account, puis Operation sont appellées dans l'ordre logique des choses (j'ai vérifié à  coup de NSLog).

    Le problème se situerait donc lors de encodeWithRootObject:, mais pourquoi ?

    Je suis désolé de poster ça comme ça et de demander une éventuelle réponse, mais là  je suis vraiment bloqué, et j'ai un besoin assez urgent de finir cette partie (Grisbi est un truc infame).

    Pour la seconde option consistant à  créer un modèle, je n'ai pas encore eu l'occasion de tester ce que j'ai commencé par manque de temps.

    Merci de vos futures réponses.
  • Philippe49Philippe49 Membre
    23:55 modifié #28
    Et en neutralisant [tableView deselectAll:self]; ?
  • MulotMulot Membre
    août 2007 modifié #29
    En fait j'avais déjà  cette ligne, que j'ai vu dans l'exemple de "Cocoa par la Pratique". Sur le coup je ne savais pas exactement pourquoi il fallait désélectionner la tableView. Cela peut provoquer des erreurs ou des comportements non souhaités lors de l'archivage ?

    Qu'il soit présent dans mon code ou non,  le "double encodage" se fait toujours.

    Est-ce qu'il y a un moyen de voir l'execution step by step dans XCode, peut être avec les breakpoint, bien que lors de crash d'applis, je n'arrive pas à  debugger convenablement.

    Par exemple quand j'ai un crash d'appli, je vois bien les méthodes appellées, mais dans la plus part des cas, je ne vois pas la ligne où l'erreur s'est produite, mais un bon vieux code assembleur, de suite ça refroidit. J'ai eu certains cas où je voyais une flèche rouge en face de l'endroit de l'erreur, mais là  encore, comportement étrange, je n'ai pas réussi à  aller dans ma méthode qui merdouillait.

    En Java il suffit de suivre les stackTrace, et on remonte anisi du main, à  la méthode contenant là  ligne où l'erreur s'est prduite etc, pour finalement arrvier à  LA ligne qui à  causé le soucis. Y a-t-il un moyen similaire avec Xcode ?

    Sinon pour en revenir au problème, comment est-ce que je peux voir chaque appel de méthode fait, pour comprendre si une méthode pourrait rappeler encodeWithRootObject ou une méthode similaire ?
  • Philippe49Philippe49 Membre
    août 2007 modifié #30
    dans 1188135684:

    Cela peut provoquer des erreurs ou des comportements non souhaités lors de l'archivage ?


    a priori non,

    dans 1188135684:

    Sinon pour en revenir au problème, comment est-ce que je peux voir chaque appel de méthode fait, pour comprendre si une méthode pourrait rappeler encodeWithRootObject ou une méthode similaire ?


    As-tu essayé la compilation avec le mode Build > Build And Debug ?

    (mettre un signet en face l'une des lignes de dataRepresentationOfType, et voit ton code s'exécuter pas à  pas)

    après tu utilises les boutons step-in et step-out qui font réaliser l'exécution pas à  pas ou par bloc (de fonction engénéral)
  • MulotMulot Membre
    23:55 modifié #31
    J'ai essayé pas à  pas, et je n'ai rien vu, l'exécution se stoppe bien au moment ou je déclare mon objet Data mais un step in, et je passe au return de ma méthode, l'attribut data à  alors une taille de 468 Octets. Donc là  je nage un peu en eaux troubles, j'y comprend pas grand chose pour débugger de façon convenable avec Xcode.

    J'ai tenté de faire ma classe myModel, mais j'ai encore quelques questions.

    Le soucis c'est que ma classe myModel tel que je la voie sera dépendante de l'UI, et ça, ça me semble pas bon du tout, puisqu'il faut que j'aille rechercher des valeurs dans des NSTextField ou des NSComboBox.

    De plus il faut que je passe mes outlets en scope public, pour que l'attribut myModel de mon controler puisse y avoir accès, ça aussi c'est ptet pas le top.

    Enfin bref là  je galère pour essayer de boucler ce petit projet bidon dans sa phase première.

    J'ai pensé à  utiliser du XML pour stocker les données, mais je pense que ça nécessitera plus de code, puisque imaginons que j'ai 2 comptes et 4 opérations dans chacun de mes comptes, il va falloir en fait que je recrée ces deux comptes et les opérations qu'ils contiennent, lors de la lecture de mon XML ?

    Ca pourrait quand même être une solution efficace de stocker ces infos via un fichier XML ? Parce que là  vu le bouzin pour essayer d'archiver un simple objet, ça me dégoutte déjà  quelque peu de l'archivage, quand on le comapre avec la simplicité de l'archivage en Java.
Connectez-vous ou Inscrivez-vous pour répondre.