[maVue addSubview:monCochon] s'affiche rien, mais [self addSubview:monCochon] si

Salut les gars,


 


je suis de nouveau bloqué avec mes cochons, pas facile ce langage .. :'(  


mais a mon avis, ca va etre simple pour vous.


 


alors voia:


-j'ai une instance d'une classe NSView nommé "maVue" qui existe. Elle dessine l'interieur d'une custom view.


 


-j'ai une instance d'un NSobject. J'aimerais qu'il demande a "maVue" d'afficher l'image d'un cochon. Donc je lui dit : 


        [maVue addSubview:monCochon]


 


mais le cochon ne s'affiche pas et j'ai un warning : " warning: 'maVue' may not respond to '+addSubview:'"


quelqu'un sait pourquoi ?


 


ca marche si j'ecris directement dans la classe maVue :


 [self addSubview:monCochon]


 


Mais alors pourquoi ne pas le faire me direz vous. :P 


Et bien parce que ce probleme en engendre beaucoup d'autres (comme un [maVue bounds] qui marche pas) etc.. 


 


j'ai mis tout mon projet en Piece Jointe

Réponses

  • berfisberfis Membre
    avril 2013 modifié #2

    maVue est une CLASSE dérivée de NSView. L'instruction



    [maVue addSubview:monCochon];

    correspond donc à  un appel à  une méthode de classe, qui devrait être héritée de NSView. Seulement voilà , NSView ne possède aucune méthode de classe (+ addview), seulement une méthode d'instance (- addview) -- comme la doc t'en convaincra aisément.


     


    ca marche si j'ecris directement dans la classe maVue :


     [self addSubview:monCochon]


     


    Non, tu ne l'écris pas dans ta classe, mais dans l'init de ton instance de classe. A ce moment-là , "self" représente cette instance, et comme addView est une méthode d'instance, ça marche effectivement.

     

    Une classe est un "moule" abstrait qui sert à  fabriquer des objets, appelés instances. Ce sont ces objets qui "existent" et avec lesquels l'application travaille. Tu dérives une classe, et tu initialises ensuite des instances de cette classe dérivée.

     

    Disons que tu veux fabriquer des gâteaux ovales afin de satisfaire ton appétit (et épater la galerie). Tu as bien une plaque à  gâteaux, mais ronde. Tu vas donc déformer une plaque ronde pour en faire une plaque ovale (=dériver la classe) et obtenir des gâteaux ovales (les instances). Mais ce que tu vas manger, ce sont les gâteaux, pas la plaque!

     

    "pas facile ce langage" : la plupart des langages orientés objets fonctionnent sur ce modèle, Objective-C n'échappe pas à  la règle. Je te suggère de commencer par te familiariser avec ces concepts avant de te lancer dans "le jeu qui déchire" avec animations, dont la réalisation pourrait s'avérer complexe. En même temps, télécharger les pdf d'Apple sur Objective-C et quelques livres de programmation sur ton iPad et passer quelques heures à  découvrir la langage et les frameworks t'en feront gagner bien plus par la suite. Moi qui te parle, j'ai eu mes meilleures idées en vacances, bien loin du clavier, avec des bouquins comme celui de Hillegass, par exemple.

     


    Courage et surtout patience!


     


    Amicalement,


    Bernard


  • MaatMaat Membre

    merci pour ta reponse Berfis !


    je lis que tu a repondu tout ca a 2h16 du matin !! ca dors pas beaucoup en Suisse ;)


     


    effectivement tu as raison j'ai confondu betement "classe derivée" et "instance de classe"


     


    je pense avoir bien saisi maintenant.


     


    j'ai reecrit mon code qui est plus logique... meme si aucun bug n'est venu de la meme ligne, d'autre sont apparus:


    IBOutlet CochonClasse * premierCochon;<


     "expected specifier-qualifier-list before 'cochonClasse'"

    IBOutlet maVueClasse * laVue;<


    "expected specifier-qualifier-list before 'maVueClasse'"

     


     


    je rappelle que mon but est simplement que l'instance d'une classe CochonClasse disent maVueClasse d'afficher l'image d'un cochon.


    ci dessous mon code simplifié:


     


    1)La classe laVueClasse:



    #import <Cocoa/Cocoa.h>
    #import "CochonClasse.h"

    @interface maVueClasse : NSView {
    IBOutlet CochonClasse * premierCochon; //<- "expected specifier-qualifier-list before 'cochonClasse'"
    }

    @end


    #import "maVueClasse.h"

    @implementation maVueClasse

    - (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
    // Initialization code here.
    premierCochon = [[CochonClasse alloc]init];
    }
    return self;
    }

    - (void)drawRect:(NSRect)dirtyRect {
    // Drawing code here.
    }

    @end
     

    2)La classe CochonClasse:



    #import <Cocoa/Cocoa.h>
    #import "maVueClasse.h"

    @interface CochonClasse : NSObject {
    IBOutlet maVueClasse * laVue; < "expected specifier-qualifier-list before 'maVueClasse'"
    }

    -(id)init;
    @end


    #import "CochonClasse.h"
    @implementation CochonClasse
    -(id)init
    {
    [super init];
    NSImageView * CochonImage = [[NSImageView alloc]init];
    [CochonImage setFrame:NSMakeRect(0, 0, 100, 100)];
    [CochonImage setImage:[NSImage imageNamed:@cochon.png]];
    [laVue addSubview:CochonImage];
    return self;
    }
    @end

    qu'en pense tu Berfis ?

     


     

     


  • berfisberfis Membre
    avril 2013 modifié #4

    Qu'un IBOutlet est l'identificateur d'une propriété, donc la syntaxe est:


     


    @property IBOutlet CochonClasse * premierCochon;


     


    Et assure-toi que ta "custom view" dans IB soit déclarée comme classe "CochonClasse"...


  • AliGatorAliGator Membre, Modérateur
    avril 2013 modifié #5

    Et au fait petit conseil au passage (j'ai pas tout lu, juste en diagonale) : respecte les conventions de nommage d'Objective-C !


    En particulier, nomme tes classes avec une majuscule (MaVueClasse et pas maVueClasse), ce sont les variables et propriétés qui commencent par une minuscule.


     


    C'est très important car cela aide énormément à  la lecture de ton code, déjà  pour nous qui n'avons pas ton projet sous les yeux mais juste des bouts de code, mais aussi pour toi à  la relecture et pour bien faire la distinction. Cela t'aurai aussi aidé à  ne pas confondre instance de classe (instance = objet avec une variable pour le stocker => commence par une minuscule) et classe (commence par une majuscule).


     


    Dans le peu que j'ai pu lire de ton code, ça me choque (et rend le code difficilement lisible de prime abord) de lire des choses comme :



    @interface maVueClasse : NSView // <-- @interface MaVueClass plutôt car tu déclares une classe, pas une instance, c'est pas une variable


    NSImageView * CochonImage = [[NSImageView alloc]init]; // <-- cochonImage plutôt car c'est une instance de classe
        [CochonImage setFrame:NSMakeRect(0, 0, 100, 100)]; // <-- du coup c'est choquant de lire ça, on a l'impression que tu appelles setFrame sur une classe (et pas un objet / une instance) du coup j'ai failli te dire qu'il y avait une erreur dans ton code

    Sinon, pour ton erreur "expected specifier-qualifier-list", c'est sans doute parce que ton fichier ne connait pas le mot-clé / le nom de classe "CochonClass" (pour le premier cas) ou "maVueClasse" (pour le second cas). Il faut faire les "#import" correspondants (ou utiliser @class si c'est juste pour déclarer) pour que les fichiers connaissent les classes déclarées dans les autres fichiers et sachent que ces classes existent et à  quoi elles correspondent.


     


     


     


    PS : berfis, pour ta réponse, ce n'est pas vraiment ça...


    1) "IBOutlet" n'est pas l'identificateur d'une propriété. C'est juste un mot clé qui n'a aucun impact fonctionnel mais qui sert juste pour que la variable ou la propriété (à  laquelle on l'a associé) soit visible dans Interface Builder (dans l'éditeur graphique d'interface quoi). Si tu ne mets pas IBOutlet, ton code marchera pareil, compilera pareil, etc, mais la propriété ("premierCochon" dans ton exemple) ne sera juste pas visible dans IB, donc tu ne pourras pas lui connecter d'objet de ton XIB. Le mot clé IBOutlet ne sert vraiment qu'à  ça : que la propriété s'affiche dans tes XIB.


    2) Dans la syntaxe qu'a utilisé Maat, il est en train de déclarer une variable d'instance. En effet, sa déclaration est entre des accolades après son @interface, emplacement pour déclarer les ivars. Donc il n'a pas à  mettre de @property dans ce cas-là , juste "IBOutlet CochonClasse* premierCochon;"... exactement comme il l'a fait, sa syntaxe est juste. L'erreur qu'il a ne vient pas de là .


     


    Après, je suis d'accord avec toi, de nos jours on ne déclare plus très souvent de variables d'instance, pour ma part je préconise plutôt de déclarer des propriétés que des variables d'instance, car en plus d'être + propre au niveau gestion mémoire (quoiqu'avec l'arrivée ARC maintenant ça ne joue plus tant que ça), cela permet de gérer le KVO et le thread-safety et même les affectations par copies sans avoir à  rajouter de ligne de code supplémentaire. Du coup je suis plutôt partisan d'utiliser des "@property IBOutlet CochonClasse* premierCochon", du coup en dehors des accolades, à  côté des déclarations de méthodes, plutôt que de déclarer une ivar "IBOutlet CochonClasse* premierCochon" entre les accolades de la classe. Et du coup d'y accéder avec "self.premierCochon". Mais bon même si je préconise d'utiliser les propriétés plutôt que les ivar, son code et sa déclaration qui utilise les ivar est tout à  fait valable. S'il a l'erreur de compilation c'est donc pour autre chose (et je pense donc pour un oubli de #import)


  • MaatMaat Membre
    avril 2013 modifié #6

    Merci pour vos reponses les gars !!


     


    Entendu pour les majuscule AliGator, j'avais lu cette convention dans le bouquin qui me guide (celui de notre cher Aarron Hillgass), mais je m'en souvenait plus.


     


    par contre j'ai ajouté @property devant le IBOutlet (dans les deux classe) et j'ai exactement la meme erreur..


     


     


    j'ai cherché longtemps dans la direction d'un probleme d'#import et Hourra !! :D  je viens de tomber ici sur un gars qui a le meme probleme que moi et notre erreur ressemble furieusement a probleme de referencement circulaire.. (ma classe A appelle la classe B...qui appelle la classe A).


     


    Je dois me barrer de chez moi tout de suite. Je fais les modif des que je rentre.. normalement je devrais m'en sortir !


    merci encore !


  • AliGatorAliGator Membre, Modérateur

    Oui si Classe A appelle Classe B qui appelle Classe A (référencement circulaire), alors il va falloir utiliser @class d'un côté pour pouvoir dire au compilateur juste "telle classe existe" sans pour autant qu'il importe le fichier .h entier.


     


    Du coup, autant quand tu fais #import "A.h" dans B.h, ça va inclure le A.h dans le fichier de B.h qui va du coup connaà®tre tous les détails de la classe A, y compris de quoi elle hérite mais surtout ses méthodes et propriétés qu'elle déclare, autant quand tu fais "@class B" dans A.h alors dans le fichier A.h il ne connaà®tra que le fait que la classe B existe, donc t'autorisera à  déclarer des variables ou des propriétés de classe B, mais il ne saura pas de quoi est composé cette classe B, en particulier de quoi elle hérite ou quelles sont les méthodes que tu peux appeler sur les instances de cette classe.


     


    Dans un fichier ".h" cela ne pose pas de problème, tu peux même utiliser "@class" plutôt que "#import" pour lister toutes les classes dont tu as besoin pour déclarer des propriétés ou variables d'instance ou pour lister les paramètres de tes méthodes, le @class suffit puisque tout ce qui lui faut c'est savoir que ce symbole représente une classe qui existe. Par contre dans le .m il te faudra faire le "#import" pour pouvoir, dans le code des méthodes (les implémentations), pouvoir utiliser les méthodes et propriétés de ces objets (sans quoi si tu fais pas #import A.h tu ne pourras pas appeler les méthodes de A sur les instances d'objets A sans qu'il te dise "je connais pas cette méthode je sais pas si les instances de A savent répondre à  ce message / connaissent cette méthode")


     


     


    Ceci dit petite note au passage, avoir des références cycliques est (pas toujours, mais souvent) signe d'une architecture mal pensée ou qui peut être améliorée. Il y a des cas où c'est inévitable, mais il y a souvent des cas où c'est parce qu'on a pas assez pensé MVC ou qu'on a pas bien structuré notre code et nos classes. Du coup quand on a une référence cyclique comme ça, il est bon de se poser et de vérifier à  2 fois si elle est vraiment justifiée ou si on aurait pas dû penser notre architecture de façon plus flexible. C'est jamais bon d'avoir des dépendances fortes entre les classes et qu'une classe ne puisse vraiment pas "vivre" sans l'autre classe et qu'elles soient liées à  ce point... (Mais bon ça ça rentrera avec le métier !)


  • berfisberfis Membre
    avril 2013 modifié #8


    Ceci dit petite note au passage, avoir des références cycliques est (pas toujours, mais souvent) signe d'une architecture mal pensée ou qui peut être améliorée.




     


    @ Aligator: Tout-à -fait d'accord, et désolé pour la boulette, je ne raisonne plus qu'en @properties depuis ARC. Quant aux "variables privées", elles étaient inconnues dans ma version de Pascal OOP, et je n'en ai jamais eu l'utilité (c'était plutôt un truc de puriste de l'encapsulation style touche-pas-à -ma-variable).


     


    @ Maat : A propos de design mal pensé, j'ai eu ton code sous les yeux, et je te suggère de tout reprendre dès le début en te demandant:


    1. Comment vais-je organiser mes données? Où stocker mes images de monstres et de cochons? De quelles données vais-je avoir besoin à  quel moment? Ce sera ta partie modèle.


    2. Qu'est-ce que l'utilisateur verra à  l'écran? Une ou plusieurs fenêtres? Pourra-t-il cliquer, si oui sur quoi? Que transmettra la partie cliquée? Pourra-t-il utiliser la barre des menus? Y aura-il des équivalent clavier? Les cochons seront-ils animés avec des layers? etc. Ce sera ta partie vues.


    3. Quand les vues apparaà®tront-elles à  l'écran? Comment seront-elles animées? dans quelle direction? Comment gérer la succession des images, quelle réponse visuelle fournira une image cliquée? En gros quels liens établiras-tu entre les données et ton utilisateur? Ce sera ta partie contrôleurs.


     


    L'utilisateur voit les vues, il interagit avec elles via les contrôleurs, mais il n'a pas accès à  la partie modèle. Tu dois t'organiser surtout en termes de rapidité d'accès (imagine un peu que tu doives charger chaque cochon depuis le disque avant de l'afficher, ce ne serait pas très fluide).


     


    Le conseil d'AliGator n'est pas un "petit plus" pour faire chic, cette histoire de majuscules fait réellement partie des conventions: un framework comme Core Data te tape sur les doigts si tu débutes une variable par une majuscule, le compilateur te bloque si tu écris SetMyVariable au lieu de setMyVariable, ou setmyVariable... alors que ta variable s'appelle myVariable, et ainsi de suite.


     


    De plus, ça t'aidera à  t'y retrouver (et nous aussi) dans ton code. Ainsi, fais des commentaires:


     


    // Remettre le cochon à  sa position de départ:


    [_monCochon setFrameOrigin: NSMakePoint(départCochon.x, departCochon.y+margeInferieure)];


     


    et évite les noms "pour faire court" qui deviennent difficiles à  comprendre:


    [_mC setFrameOrigin: NSMakePoint(dC.x,dC.y + mInf)];


     


    N'oublie pas que celui qui relira le code dans deux mois pour trouver ce qui cloche... a de fortes chances d'être toi-même, qui ne se souviendra plus de ce que voulais dire telle ou telle instruction.


     


    Au risque de paraà®tre prosélyte à  la communauté CocoaCafé, je pense que ARC + Modern Objective-C sont des progrès qui non seulement simplifient l'écriture et la rendent lisible, mais aussi permettent d'éviter les zombies et autres plantages dus à  une gestion alambiquée de la mémoire.


     


    Dix ans de Pascal à  envoyer mon programme dans la 4e dimension à  cause d'un HLock oublié --et à  l'époque c'était l'icône bombe et on redémarrait le Mac-- merci, j'ai donné, donc loué soit ARC.


     


    Mais bon, c'est clair que si on doit faire tourner des app sur Snow Leopard ou même Lion, il y a des "progrès" auxquels on doit renoncer (comme la géniale Base Localization, par exemple) et je suis dans cette situation.


     


    Bonne chance aux Cochons dans l'espace!


    Bernard


  • MaatMaat Membre

    merci pour vos explications les gars ! o:)   toujours tres clair !


     


    effectivement, vous avez raison. le probleme vient que j'ai des le debut pas pensé MVC.


     


    Pas pensé du tout car j'ai ecris tout le code dans une sous-classe de NSView. Puis je me suis rendu compte que je n'utilisais pas trop les spécificités d'un langage orienté objet.


     


    Alors j'ai divisé mon code comme un gros porc (et encore j'ai vu des gros porcs qui divisaient un code mieux que ca  :P ). J'imagine tres bien vos tetes depitées en lisant ces lignes :) mais bon, fallait que je le fasse une fois dans ma vie..


     


    J'ai tout reecrit en pensant MVC et ca marche.


     


    Promis je vous file le jeu des qu'il est fini et la les cochons dans l'espace feront moins les malins !!  :p


  • AliGatorAliGator Membre, Modérateur


     


    Alors j'ai divisé mon code comme un gros porc




    En même temps, c'est logique vu le thème de ton jeu, ça reste avec les cochons :D :P

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