Relier deux NIB dans Interface Builder ?

ChachaChacha Membre
Bonjour,

Et oui, encore un problème (mais mon appli avance bien malgré tout). Alors voilà  : je fais une appli multidocuments. J'ai donc un MainMenu.nib et un MyDocument.nib. Jusque là , ça va.
Or, j'ai besoin de centraliser des informations partagées par tous les documents.
Il m'a donc semblé judicieux de créer une classe AppController (rien à  voir avec les contrôleurs des Bindings, hein), et de l'instancier dans MainMenu.nib. Je suis donc assuré de l'unicité de cet objet, et compte le connecter par outlet à  mes objets de type MyDocument.
Dans un premier temps, cet objet AppController me sert comme delegate du menu pour le validateMenuItem (je dis ça pour ceux qui connaissent).
Et maintenant, innocemment, dans ma classe MyDocument, je crée un IBOutlet vers un AppController*. Et là , magie, avec le contrôle click, je relie le File's Owner de MyDocument.nib avec l'instance de AppController dans MainMenu.nib. Vous suivez toujours ?

Et bien au début, ça marchait. L'appli se comportait correctement, chouette. Et puis j'ai fermé et rouvert MyDocument.nib, et là , Aà¯e ! Ce lien inter-nib cause une "inconsistency" dans MyDocument.nib, et est supprimé à  chaque fois que je le ferme/réouvre.

Avec la politique de l'autruche, je pourrais faire comme si de rien n'était, et recréer le lien à  chaque fois. Mais tout d'à  coup, ça c'est mis à  ne plus marcher du tout, et l'appli ne se lance alors plus, en générant un message bizarre: -[NSCFNumber isEqualToString:]: selector not recognized
Supprimer le lien inter-NIB corrige le problème (l'appli se lance), mais du coup mon outlet n'est plus connectée, et je pers les fonctionnalités associées.

Ma question est donc double : est-il possible de relier des NIB entre eux, ou faut-il oublier ? Dans le second cas, comment connecter mon AppController à  mes objets MyDocument ?

Si je suis obligé de créer leon dans IB, j'ai un début de solution, que je soumet à  votre sagacité :
  -je fais en sorte que mon AppController ne me serve plus que comme delegate du MainMenu pour le validateMenuItem
  -je crée une autre classe du genre CentralizedInfo, que j'instancie dans le init de MyDocument en utilisant un pattern singleton pour n'en avoir qu'une instance.

Que pensez-vous de ce mic-mac ?

+
Chacha

Réponses

  • BruBru Membre
    20:54 modifié #2
    Quand tu parles de 2 nib, tu évoques bien 2 fichiers nib dont l'un est au moins chargé dans ton programme par un truc du style loadNib... ?

    Si c'est oui, alors il n'y a aucun moyen de les relier via les classiques outlets dans IB.

    Par contre si ton AppController est le owner du second fichier nib, alors là  tous tes problèmes se résolvent.

    Dans le second fichier nib, change le owner du nib (icone "file's owner")  : par défaut, le owner est de classe NSApplication. Dans ton cas, importe le .h (par drag'n drop du fichier dans Xcode vers la fenêtre de IB) de AppController. Ensuite, après avoir sélectionné l'icône file's owner, affiche l'inspecteur et choisis Custom Class, enfin sélectionne ta classe AppController.

    Maintenant tu peux relier tous tes objets dans le second nib vers le file's owner (dont tu verras les outlets).

    Quand le loadNib... sera fait, les outlets seront automatiquement reliés aux objets de ce second nib !

    .
  • ChachaChacha Membre
    20:54 modifié #3
    Bonjour,

    dans 1108422681:

    Quand tu parles de 2 nib, tu évoques bien 2 fichiers nib dont l'un est au moins chargé dans ton programme par un truc du style loadNib... ?


    Tu veux dire, est-ce que je charge un des NIB dynamiquement ? Non, ça je n'ai jamais fait. Il s'agit ici des deux NIB par défaut (MainMenu.nib et MyDocument.nib) créés par XCode quand on démarre une application multi-documents.


    Si c'est oui, alors il n'y a aucun moyen de les relier via les classiques outlets dans IB.

    Dommage.


    Par contre si ton AppController est le owner du second fichier nib, alors là  tous tes problèmes se résolvent.


    Mmmh... j'avoue que j'ai encore du mal avec ce qu'est un File's owner. D'après la doc : "The File's Owner represents the object that manages the nib file and is not actually in the nib file. You specify it when you load the nib file. Use it when you need to make connections to objects outside the nib file." Puis-je me dire que le File's owner, c'est simplement l'objet avec qui sont connectés les outlets extérieures du NIB ?


    Dans le second fichier nib, change le owner du nib (icone "file's owner")  : par défaut, le owner est de classe NSApplication. Dans ton cas, importe le .h (par drag'n drop du fichier dans Xcode vers la fenêtre de IB) de AppController. Ensuite, après avoir sélectionné l'icône file's owner, affiche l'inspecteur et choisis Custom Class, enfin sélectionne ta classe AppController.

    D'abord, juste un détail : est-ce important que le File's Owner de MainMenu.nib dérive de NSApplication ? Car si je veux imposer mon AppController, qui ne dérive pas de NSApplication, IB me demande si je suis vraiment sûr de vouloir ça. Moi je veux bien faire dériver mon AppController de NSApplication, mais je voudrais comprendre ce que ça cache, et pourquoi il insiste sur ce point.

    Ensuite, voici ce que je comprend finalement de ma situation:
      -le File's Owner de MyDocument.nib n'a pas tellement d'autres solutions que d'être la classe MyDocument elle-même.
      -puisque MainMenu.nib est chargé automatiquement, je n'ai pas d'espoir, en lui assignant un File's Owner personnalisé, de résoudre mon problème.

    Je vais donc devoir passer par quelques lignes de code pour établir des connexions. Mais me vient alors une question : depuis une méthode de MyDocument, comment puis-je avoir accès, comment puis-je "voir", un objet contenu dans MainMenu.nib (à  savoir l'instance de AppController que je vais y créer) ?

    +
    Chacha
  • mpergandmpergand Membre
    février 2005 modifié #4
    Je crois que ton problème vient que tu veux faire jouer à  ton objet appDelegate deux rôle différents. Si je te suis bien:
    Or, j'ai besoin de centraliser des informations partagées par tous les documents.

    tu as besoin d'un object unique qui puisse être accessible de n'importe où dans ton appli. C'est une situation très banale et on appelle ça un singleton : Singleton

    Un exemple typique d'utilisation d'un singleton est la gestion des préférences globales de l'appli: objet unique à  toute l'appli et accessible par tous autres objets.

    Ce principe est, naturellement, très utilisé par Cocoa, ex:
    [NSApplication sharedApplication];

    qui retourne l'instance unique (bien sûr) de NSApplication créée au démarrage du programme.

    Une méthode moins sophistiquée de faire les choses, consiste tout simplement à  définir des méthodes de classes (+)  ;)

    [edit]
    -je crée une autre classe du genre CentralizedInfo, que j'instancie dans le init de MyDocument en utilisant un pattern singleton pour n'en avoir qu'une instance.

    milles excuses j'avais pas vu  ::) tu peux créer cette instance au démarrage de l'appli, mais ce n'est peut-être pas adapté à  ton cas.
  • ChachaChacha Membre
    20:54 modifié #5
    Je continue dans ce thread, mais peut-être l'erreur que je rencontre en mérite-t-elle un neuf ?


    tu as besoin d'un object unique qui puisse être accessible de n'importe où dans ton appli. C'est une situation très banale et on appelle ça un singleton : Singleton


    Pas de problème, j'ai recopié le singleton d'Apple, et je le comprends très bien (c'est juste un peu plus compliqué qu'un singleton C++). Sauf que (et oui, rien n'est parfait), j'ai un gros souci. Je peux le résoudre, comme vous verrez, mais avant je voudrais comprendre pourquoi il y a souci.

    J'introduis juste mes notations pour la suite des explications :
    static AppController* uniqueInstance;
    est ma véritable variable encapsulée par le singleton.
    Je l'obtiens par un appel à 
    +appController;

    Voilà  le souci : mon AppController est instancié dans mainmenu.nib, donc son init n'est jamais appelé; par conséquent, la variable statique uniqueInstance reste à  nil, jusqu'au premier appel à  [AppController appController], qui est censé me renvoyer mon instance unique. Ok, je vous accorde que pour comprendre cette phrase, il vaut mieux avoir le singleton Objective-C sous les yeux.

    Comme la variable était à  nil, hop, ce premier appel crée un nouvel AppController ! Je me retrouve donc avec deux instances : celle de MainMenu.nib, et celle que je viens de créer par l'appel à  l'accesseur.

    Je me suis donc dit qu'aussi, c'était bien ma faute. Et j'ai rajouté
    uniqueInstance = self;
    dans le -awakeFromNib de AppController. De cette façon, j'initialise uniqueInstance avant le premier appel à  [AppController appController], et ces futurs appels me renverront bien mon objet instancié dans MainMenu.nib

    Et bien bizarrement, dans ce cas, juste en ayant rajouté cette ligne, l'appli ne se lance pas, avec le message d'erreur:
    -[NSCFNumber isEqualToString:]: selector not recognized

    Histoire de, j'ai essayé de mettre à  la place (toujours dans le awakeFromNib)
    self = [AppController appController];
    Mais bon, l'erreur est la même au lancement du programme.

    Non mais qu'est ce que ça signifie ?

    La solution (toute bête) : ne plus instancier AppController dans MainMenu.nib.
    mais avant ça, j'aimerais bien comprendre...

    +
    Chacha, qui aime avoir des problèmes bizarres
  • mpergandmpergand Membre
    février 2005 modifié #6
    Pas sûr à  100 % mais je crois qu c'est initWithCoder qui est appelé...

    Je me suis donc dit qu'aussi, c'était bien ma faute. Et j'ai rajouté
    uniqueInstance = self;
    dans le -awakeFromNib de AppController.

    Le problème est qu'on ne peut pas savoir dans quel ordre les awakeFromNib sont appelés, si dans un autre awakeFromNib, tu fais référence à  uniqueInstance, il se peut que celui-ci ne soit pas encore initialisé.

    La solution (toute bête) : ne plus instancier AppController dans MainMenu.nib.


    T'as raison, car c'est vraiment zarbi de faire ça  pour un singleton :o
  • BruBru Membre
    20:54 modifié #7
    Juste une question : où as tu mis le code qui te ramène la singleton ? (le truc du style [tt]appcontr=[AppController appController][/tt])

    .
  • ChachaChacha Membre
    février 2005 modifié #8
    dans 1108478082:

    Juste une question : où as tu mis le code qui te ramène la singleton ? (le truc du style [tt]appcontr=[AppController appController][/tt])


    Et bien comme ce sont mes objets MyDocument qui ont besoin de connaà®tre le AppController, ce sont dans les méthodes de MyDocument qu'on trouve des appels à  [AppController appController].
    J'ai peur qu'on commence à  s'embrouiller, vous voulez que je fasse un petit résumé sous forme d'un code minimal ?

    [plus tard...]
    Bon, j'ai fait un code minimal; il marche. Donc mes erreurs bizarres viennent d'ailleurs. Désolé.

    Mon code minimal:
    -Une appli multi-documents de base.
    -Une classe AppController qui contient tout le code made in Apple pour en faire un singleton.
    -Une instance de AppController dans MainMenu.nib
    -je rajoute dans AppController.m :
        -(void)awakeFromNib
      {
          uniqueInstance = self; //unique instance est le nom de la variable statique
                                            //encapsulée par le singleton
      }

    Et bien maintenant, si je réclame [AppController appController] dans une méthode de MyDocument, j'obtiens bien mon instance contenue dans le MainMenu.nib (j'ai vérifié les adresses). Donc un singleton peut bien être instancié dans un fichier NIB.

    +
    Chacha
  • mpergandmpergand Membre
    20:54 modifié #9
    Super, du coup dans appController, tu as juste besoin de faire:

    return uniqueInstance;

    direct  ;D
  • ChachaChacha Membre
    20:54 modifié #10
    dans 1108488838:

    Super, du coup dans appController, tu as juste besoin de faire:

    return uniqueInstance;

    direct  ;D


    Tiens ?! C'est carrément pas bête, ça. Bon, ça n'est plus un singleton, car rien ne m'empêche d'instancier une nouvelle fois l'objet avec alloc init, mais dans mon cas ce sera bien suffisant.

    Sinon, ça y est, je viens de résoudre tous mes problèmes et mon appli remarche bien. Je propose donc de clore ce thread sur cette bonne nouvelle, en résumant ce qu'on y a appris :
    -on ne peut pas relier deux fichiers NIB n'importe comment dans Interface Builder.
    -on peut faire un singleton avec un objet instancié dans un .NIB

    En vous remerciant encore.

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