awakeFromNib

RocouRocou Membre
23:40 modifié dans API AppKit #1
C'est encore moi  :why?:

J'ai deux méthodes awakeFromNib (une dans mon contrôleur et une dans le code d'une vue).

Comment être sûr que la méthode awakeFromNib de mon contrôleur démarre avant celle de ma vue?

Réponses

  • GreensourceGreensource Membre
    23:40 modifié #2
    Comme ça je sais pas, il me semble qu'on ne peut pas vraiment savoir justement, l'effet boite noire d'IB.

    Mais du coup, si c'est ça, ça pose la question suivante: es-tu sûr de la conception de ton application?
  • CéroceCéroce Membre, Modérateur
    23:40 modifié #3
    Je crois qu'on ne peut pas.

    Vois l'autre post et la remarque mpergand sur le MVC. Si ton programme dépend de l'ordre d'appel, alors tu as créé des dépendances entre objets. Tout l'enjeu de la programmation objet est justement de les limiter.

    (je sais, ce que j'écris est très généraliste, mais si on s'ennuie à  étudier les Design Patterns et le remaniement du code, il y a une bonne raison).
  • RocouRocou Membre
    23:40 modifié #4
    dans 1246463799:

    Je crois qu'on ne peut pas.

    Vois l'autre post et la remarque mpergand sur le MVC. Si ton programme dépend de l'ordre d'appel, alors tu as créé des dépendances entre objets. Tout l'enjeu de la programmation objet est justement de les limiter.

    C'est bien ce que je pensais. Comme le souligne GreenSource, je ne suis pas sûr du tout de la conception de mon application.

    Au début, j'avais mis tout le code dans ma vue. Puis on m'a fait remarquer que je n'avais pas de contrôleur. Depuis, j'essaie de faire cohabiter contrôleur et vue mais cela complique beaucoup les choses. Je ne vois pas du tout l'intérêt du contrôleur dans la mesure où toute mon application est liée à  ce qui se passe dans ma vue.
  • mpergandmpergand Membre
    juillet 2009 modifié #5
    dans 1246464853:

    dans 1246463799:

    Je crois qu'on ne peut pas.

    Vois l'autre post et la remarque mpergand sur le MVC. Si ton programme dépend de l'ordre d'appel, alors tu as créé des dépendances entre objets. Tout l'enjeu de la programmation objet est justement de les limiter.

    C'est bien ce que je pensais. Comme le souligne GreenSource, je ne suis pas sûr du tout de la conception de mon application.

    Au début, j'avais mis tout le code dans ma vue. Puis on m'a fait remarquer que je n'avais pas de contrôleur. Depuis, j'essaie de faire cohabiter contrôleur et vue mais cela complique beaucoup les choses. Je ne vois pas du tout l'intérêt du contrôleur dans la mesure où toute mon application est liée à  ce qui se passe dans ma vue.


    OK, on touche le point sensible  ;)

    Je vais spéculer dans l'abstrait, car je ne connais pas ton prog...

    Si j'ai bien suivi, tu as une vue  maà®tre qui gère d'autres vues. C'est en fait une sorte de contrôleur de vues !
    Tu as donc créé un View-Controller. Ce n'est pas une hérésie, NSDocument est bien un Model-Controller.

    L'important ici c'est d'être logique,  un type d'actions doit être géré soit par le View-Controller, soit par le Controller général de l'appli, mais pas par les deux à  la fois.

    Ou encore, tu crées deux objets Contrôleurs différents, un général et un spécial pour la vue maà®tre. Et c'est le contrôleur général qui communique avec le contrôleur de la vue.

  • psychoh13psychoh13 Mothership Developer Membre
    23:40 modifié #6
    Du côté de -awakeFromNib, la documentation spécifie clairement que l'ordre d'envoi de ce message est indéfini et qu'on ne devrait pas se baser là -dessus.
  • CéroceCéroce Membre, Modérateur
    23:40 modifié #7
    dans 1246464853:

    Au début, j'avais mis tout le code dans ma vue. Puis on m'a fait remarquer que je n'avais pas de contrôleur. Depuis, j'essaie de faire cohabiter contrôleur et vue mais cela complique beaucoup les choses. Je ne vois pas du tout l'intérêt du contrôleur dans la mesure où toute mon application est liée à  ce qui se passe dans ma vue.


    Le premier intérêt à  ton niveau, c'est déjà  d'avoir le code de ta classe Vue faire moins de 10 000 lignes !
    Je vais tenter de te donner un exemple pour que tu comprennes l'intérêt du MVC, qui sépare les données (Modèle) de leur représentation (Vue).

    Pour l'instant, tu disposes d'un seul type de vue, qui affiche les tâches sous forme de diagramme de Gant. Imagine maintenant que tu veuilles une vue sous forme de calendrier. Tu peux remarquer que le modèle ne change pas. En utilisant le MVC, tu n'as qu'à  instancier une nouvelle vue et la relier au modèle via le contrôleur.

    Prenons l'exemple inverse: tu veux un diagramme de Gant qui représente un autre modèle. Comme tu utilises le MVC, ta vue Diagramme de Gant sera réutilisable dans tout programme.

    Le contrôleur sert à  faire les liens entre Vue et Modèle; de fait, c'est la partie la moins réutilisable, mais souvent la plus facile à  coder.
  • RocouRocou Membre
    23:40 modifié #8
    dans 1246516422:

    Je vais tenter de te donner un exemple pour que tu comprennes l'intérêt du MVC, qui sépare les données (Modèle) de leur représentation (Vue).


    Merci à  tous pour vos explications.
    En théorie, je pense avoir bien compris le MVC mais quand je passe à  la pratique et dans le détail, c'est moins clair.
    Je mettrais bien mon application en pièce jointe mais elle est maintenant très liée à  une base de données PostgreSQL.

    Voici justement un exemple de confusion: mon application passe son temps à  faire des accès à  la base de données. La vue (qui est effectivement une sorte de diagramme de gantt) est une représentation de ces données.

    Par exemple, la connexion à  la base de données doit-elle se faire dans le code du contrôleur ou dans celui de la vue? Cet exemple est précisément ce qui a généré le sujet de ce fil: si la connexion se fait dans le awakeFromNib du contrôleur, je ne dois surtout pas faire d'accès à  la base dans le awakeFromNib de la vue car je ne suis pas sûr que la connexion soit faite.

    Autre exemple: un clic droit dans ma vue met en avant plan un NSPanel qui affiche les données exhaustives d'un élément du "diagramme de Gantt" de ma vue. Le contrôle  de ce NSPanel doit-il se faire par la vue ou par le contrôleur?*

    En toute logique, ça devrait être le contrôleur mais dans ce cas, la vue doit lui passer des paramètres ce qui "casse" un peu le MVC, non?
    (et puis, je ne sais vraiment comment procéder pour faire cela..)


    *Exemple dans l'exemple:
    Dans mon NSPanel, un NSPopUpButton doit être initialisé avec une liste d'items récupérés dans la base de données (en l'occurrence il s'agit d'une liste de clients).
    Quand l'utilisateur sélectionne un nom de client, un NSTextField est initialisé avec l'adresse de ce client.

    C'est facile, c'est géré par le contrôleur.

    Mais quand l'utilisateur fait un clic droit sur un élément de la vue, ce fameux NSPopUpButton doit être positionné sur le client correspondant et le NSTextField doit être initialisé avec l'adresse de ce client.

    J'ai une méthode qui récupère et retourne l'adresse d'un client. Cette méthode est appelé par la vue et par le contrôleur.
    Comme vous le voyez, tout est très imbriqué et j'ai vraiment du mal à  concevoir une application "MVC".
  • GreensourceGreensource Membre
    23:40 modifié #9
    Je vais essayer d'expliquer avec ce que je sais pour l'instant et du peu d'expérience que j'ai avec MVC :P

    Pour ce qui est de ta connexion SQL, elle est à  faire dans le contrôleur. En effet ta vue n'a pas à  connaà®tre ta base de donnée puisque c'est ton modèle.
    Après est-ce que c'est à  faire dans le awakeFromNib je ne sais pas trop.

    Pour ce qui est de MVC plus généralement et en reprenant ton exemple. Moi je vois les choses comme ça: ta vue ne fait que de l'affichage et détection des actions utilisateurs. Donc elle doit juste fournir des services du genre "afficher mon NSPanel". Ca c'est pour l'affichage et sinon pour les détection des actions utilisateurs le Contrôleur doit te fournir les services adéquats. C'est à  dire par exemple: "clic droit détecter".

    Ensuite tu va avoir tout un jeu de communication entre Vue-Contrôleur-Modèle.
    User(clic droit) -> Controller(Clic droit détecté:ici) -> Vue(affiche Panel:ici) -> Model(récupérer donnée) ->Vue(afficher les données dans le Panel)...

    Je sais pas si je suis bien clair, un diagramme de séquence parlerais mieux je penses mais j'ai pas de soft sous la main.
  • mpergandmpergand Membre
    juillet 2009 modifié #10
    J'ai une méthode qui récupère et retourne l'adresse d'un client. Cette méthode est appelé par la vue et par le contrôleur.
    Comme vous le voyez, tout est très imbriqué et j'ai vraiment du mal à  concevoir une application "MVC".


    C'est là  le problème, tu n'as pas défini assez précisément  le rôle de tes objets.

    Dans le modèle MVC, une vue n'a pas accès au modèle, c'est un élément passif, il prévient quand son état a changé: l'utilisateur a cliqué sur cette vue, la vue a changé de place, de taille, etc.

    Dans le cas d'une vue très spécialisée et spécifique à  une appli, il n'est pas incongru de lui faire jouer aussi le rôle de contrôleur, c'est à  dire qu'elle peut accéder au modèle directement.

    Mais ça n'autorise pas à  faire n'importe quoi, et le problème du awakeFromNib en est un exemple.

    awakeFromNib sert à  initialiser les objets de façon indépendantes, mais si ces objets dépendent des uns des autres, ça marche plus, il faut un objet maitre qui ordonne tout ça.

    En pratique ça peut donner ça:
    contrôleurGéné = contrôleur général de l'appli
    contrôleurVue = contôleur de la vue

    dans awakeFromNib du contrôleurGéné:

    [contrôleurVue initialiseVue];

    C'est le contrôleurGéné qui donne des ordres au contrôleurVue pas l'inverse.

    Il faut établir une hiérarchie, chaque objet ayant une tache bien définie.
    contrôleurGéné->contrôleurVue->Vue

    Le contrôleurGéné et la Vue ne doivent pas se connaà®tre, c'est le rôle du contrôleurVue de faire la liaison entre les deux si nécessaire.

  • CéroceCéroce Membre, Modérateur
    23:40 modifié #11
    dans 1246519534:

    Par exemple, la connexion à  la base de données doit-elle se faire dans le code du contrôleur ou dans celui de la vue?


    Ni l'un, ni l'autre.
    Dans le modèle ! Le modèle contient les données et effectue les calculs. Le modèle va lire les données dans la base quand on les lui réclame; il fait l'interface avec les autres objets qui n'ont pas à  envoyer de requêtes SQL.

    dans 1246519534:

    Autre exemple: un clic droit dans ma vue met en avant plan un NSPanel qui affiche les données exhaustives d'un élément du "diagramme de Gantt" de ma vue. Le contrôle  de ce NSPanel doit-il se faire par la vue ou par le contrôleur?


    Par un autre contrôleur (héritier de NSWindowController), qui ne gérerait que ce NSPanel.

    dans 1246519534:

    En toute logique, ça devrait être le contrôleur mais dans ce cas, la vue doit lui passer des paramètres ce qui "casse" un peu le MVC, non?


    Non, puisque c'est le modèle qui contient les données. La difficulté est justement d'échanger ces données entre la VueGantt qui a besoin de savoir:
    - quel est le mois courant
    - la liste des tâches pour ce mois

    et le modèle qui contient:
    - toutes les tâches pour tous les mois de toutes les années
    (le modèle n'a pas de notion de mois courant)

    On peut voir que la donnée de base est la tâche:  date de début, date de fin, catégorie, nom du client.


    Comme je l'ai écris précédemment, inspire-toi de NSTableView: cette classe affiche un tableau sans avoir aucune connaissance du modèle. Elle se contente de réclamer le contenu d'une cellule (le plus souvent du texte) quand elle doit l'afficher.


    dans 1246519534:

    Dans mon NSPanel, un NSPopUpButton doit être initialisé avec une liste d'items récupérés dans la base de données (en l'occurrence il s'agit d'une liste de clients).
    Quand l'utilisateur sélectionne un nom de client, un NSTextField est initialisé avec l'adresse de ce client.


    Dans cet exemple:
    - À l'ouverture du panneau, le contrôleur demande au modèle la liste des clients et l'affiche dans le pop-up.
    - Quand un article du pop-up est cliqué, le contrôleur qui est la cible, demande au pop-up l'indice de l'article de menu
    - Le contrôleur demande alors au modèle les données pour le client à  cet indice.

    dans 1246519534:

    Mais quand l'utilisateur fait un clic droit sur un élément de la vue, ce fameux NSPopUpButton doit être positionné sur le client correspondant et le NSTextField doit être initialisé avec l'adresse de ce client.


    - La vue envoie une action "clic droit" au contrôleur
    - Le contrôleur demande à  la vue quelle est la tâche actuellement sélectionnée.
    - Le contrôleur demande au modèle quel est le client de cette tâche et tu retrouves dans le cas précédent.
  • RocouRocou Membre
    23:40 modifié #12
    dans 1246525966:

    dans 1246519534:

    Par exemple, la connexion à  la base de données doit-elle se faire dans le code du contrôleur ou dans celui de la vue?


    Ni l'un, ni l'autre.
    Dans le modèle ! Le modèle contient les données et effectue les calculs. Le modèle va lire les données dans la base quand on les lui réclame; il fait l'interface avec les autres objets qui n'ont pas à  envoyer de requêtes SQL.


    J'ai repris le bouquin d'Hillgass (3e édition), au chapitre 8 page 125, il est dit:
    "un objet contrôleur lit le modèle depuis un fichier ou une base de données, puis l'affiche à  l'aide de la vue"

    Que signifie "lire le modèle depuis une base de données"?  B)

    Dois-je écrire une nouvelle classe (ou plusieurs), le fameux modèle, qui contiendra l'ensemble de méthodes de lecture/écriture de mes données dans la base? Faut-il que cette classe contienne littéralement les données ou peut-elle se contenter de les récupérer dans la base selon les besoins?
    Si je fais le parallèle avec une NSTableView, il faudrait que le modèle contienne les données.
    Peut-on par conséquent comparer un modèle avec un datasource?

    Il va falloir que je relise ce chapitre 8 sur lequel j'avais fait une impasse  :'(
  • GreensourceGreensource Membre
    23:40 modifié #13
    Je lirais même le chapitre sur Core Data à  ta place le 11. M'est avis que ça pourrais servir même si je ne l'ai pas encore fait moi non plus ;-)
  • RocouRocou Membre
    23:40 modifié #14

    - La vue envoie une action "clic droit" au contrôleur

    Comment fait-on cela?

    Dans ma vue j'ai la méthode
    -(void) rightMouseDown:(NSEvent*)monEvenement<br />{...}
    

    ça veut dire quoi "envoyer une action"?
    Comment une vue peut-elle envoyer une information à  un contrôleur?

    Jusqu'à  maintenant, dans le code ci-dessus, j'affichais mon NSPanel. Une fois celui-ci ouvert, c'est facile, je déclare des outlets dans mon contrôleur et dans ma vue et je fais les liaisons sous IB. Ainsi chaque objet contenu dans le NSPanel est relié à  la fois à  la vue et au contrôleur.

    A te lire, il semble qu'on puisse réaliser d'autres types de liaisons? Mais comment?
  • RocouRocou Membre
    23:40 modifié #15
    dans 1246538660:

    Je lirais même le chapitre sur Core Data à  ta place le 11. M'est avis que ça pourrais servir même si je ne l'ai pas encore fait moi non plus ;-)


    Effectivement, a première vue, ça ce rapproche beaucoup de ce que je voudrais faire  :)
  • mpergandmpergand Membre
    23:40 modifié #16
    ça veut dire quoi "envoyer une action"?
    Comment une vue peut-elle envoyer une information à  un contrôleur?


    Ca veut dire: coucou ! c'est moi, l'utilisateur a cliqué sur le bouton droit de la souris, ça t'intéresse ?

    En pratique, ça consiste à  appeler une méthode du contrôleur.

    Et tu peux définir autant d'actions que tu veux. Tu a juste à  décider du nom des différentes méthodes qui seront appelées dans le contrôleur. Comme un protocole, mais là  en fait, on est plus proche du type data source.
  • RocouRocou Membre
    23:40 modifié #17
    dans 1246541181:

    ça veut dire quoi "envoyer une action"?
    Comment une vue peut-elle envoyer une information à  un contrôleur?


    Ca veut dire: coucou ! c'est moi, l'utilisateur a cliqué sur le bouton droit de la souris, ça t'intéresse ?

    En pratique, ça consiste à  appeler une méthode du contrôleur.

    Ok, c'est plus clair  :)

    ... Mais je n'ai jamais réussi à  le faire.
    [appControleur maMéthode] ne se compile pas puisque maMethode est déclarée dans le contrôleur et pas dans la vue.
  • mpergandmpergand Membre
    juillet 2009 modifié #18
    Un exemple ultra vite fait d'un MultiActions/Datasource  ;)

    Et sacrilège  :o  dans mon exemple MainView est un View-Controller  :)


  • RocouRocou Membre
    23:40 modifié #19
    dans 1246544684:

    Un exemple ultra vite fait d'un MultiActions/Datasource  ;)

    Et sacrilège  :o  dans mon exemple MainView est un View-Controller  :)

    Un grand merci.
    Il faut juste que je me penche sur la directive 'protocol' que je maà®trise pas et qui rend ton code -pourtant très court- pas très lisible pour moi  :)
  • mpergandmpergand Membre
    juillet 2009 modifié #20
    Le Controller doit être conforme au protocole DataSourceProtocol, c'est à  dire qu'il doit implémenter les méthodes de ce protocole.

    En fait, j'aurais dû déclarer le Controller comme ceci:
    @interface Controller : NSObject <DataSourceProtocol>

    Ce que je n'ai pas fait, car c'est pas ça l'important, le plus important c'est dans MainView:
    id<DataSourceProtocol> dataSource;

    Ca permet au compilateur de connaà®tre les méthodes du DataSourceProtocol et donc dans le code d'appeler ces méthodes directement (et clouer le bec au compilo  ;))

    Sinon en dynamique, il faut passer par un performSelector et c'est bien plus lourd à  gérer, surtout pour le paramètre de retour !
  • RocouRocou Membre
    juillet 2009 modifié #21
    dans 1246556916:

    Le Controller doit être conforme au protocole DataSourceProtocol, c'est à  dire qu'il doit implémenter les méthodes de ce protocole.

    En fait, j'aurais dû déclarer le Controller comme ceci:
    @interface Controller : NSObject <DataSourceProtocol>

    Ce que je n'ai pas fait, car c'est pas ça l'important, le plus important c'est dans MainView:
    id<DataSourceProtocol> dataSource;

    Ca permet au compilateur de connaà®tre les méthodes du DataSourceProtocol et donc dans le code d'appeler ces méthodes directement (et clouer le bec au compilo  ;))

    Si je comprends bien, toutes les méthodes déclarées dans le DataSourceProtocol peuvent être appelées dans n'importe quelle classe (à  condition d'avoir ajouté id<DataSourceProtocol> dataSource;)?

    EDIT: après test, non ça ne fonctionne pas comme je l'espérais  :(
  • mpergandmpergand Membre
    juillet 2009 modifié #22
    Les protocoles, tout comme les fichiers headers (.h) déclarent des méthodes au compilo.
    Quand je déclare qu'une classe est conforme à  tel protocole, le compilo suppose qu'elle répond à  ces méthodes. (donc pas de warning)

    Mais si ces méthodes n'existent pas, ça plante à  l'exécution, normal !

    Dans mon exemple, je n'ai pas déclaré Controller conforme au protocole DataSourceProtocol, j'ai trompé le compilo en faisant un cast:
    [mainView setDataSource:(<DataSourceProtocol>)self];

    Même si le Controller n'implémente pas les méthodes du protocole, le compilo ne verra rien, mais bien sûr ça va planter à  l'exécution.

    Donc dans MainView la déclaration:
    id<DataSourceProtocol> dataSource;

    ne sert qu'à  informer le compilo que dataSource est un objet de type non défini, mais qui implémente les méthodes du protocole, donc je peux faire:
    int number=[dataSource numberOfUsers];
    Le complilo est content même si la méthode n'existe pas.

    Pour éviter les plantages à  l'exécution, on va déclarer formellement que la classe Controller doit être conforme au protocole.
    @interface Controller : NSObject <DataSourceProtocol>

    Si tu n'implémentes pas ces méthodes le compilo va t'insulter !
    C'est juste une sécurité pour toi le programmeur.
    Et c'est ce qu'on appelle, logiquement, un protocole formel.

    Il existe aussi des protocoles non formels, mais c'est une autre histoire  ;)


  • CéroceCéroce Membre, Modérateur
    23:40 modifié #23
    Un protocole est un moyen de définir les obligations d'une classe.
    Si une classe déclare respecter un protocole, alors elle se doit de définir toutes les méthodes de ce protocole*.

    Si elle ne le fait pas, le compilateur le signale.
    Lis le chapitre qui leur est consacré dans le guide ObjC d'Apple; pour une fois c'est plutôt clair et concis.


    Pour revenir aux actions: c'est ce que tu utilises depuis le début: tu sais, les méthodes précédées de IBAction.
    mpergand est plutôt parti sur de la délégation, ce qui est plus judicieux dans ce cas. La vue appelle des méthodes de son délégué pour lui signaler des modifications ou obtenir des données. Les datasources sont une forme de délégation. En POO, c'est comme en entreprise, il faut savoir déléguer !


    * C'est l'idée première, mais il existe les protocoles informels et les méthodes optionnelles.
Connectez-vous ou Inscrivez-vous pour répondre.