Se débarasser de certains warnings

Bonjour,

J'utilise dans mon code un certain nombre d'objets sans les typer statiquement (déclaration avec le mot clé id). Comme dans le code qui suit :

<br />NSEnumerator *enumerator = [modifiers objectEnumerator];<br />id modifier;<br />while (modifier = [enumerator nextObject])<br />{<br />&nbsp; if ([modifier respondsToSelector: @selector(alterCroissance)])	c += (int)[modifier alterCroissance];<br />}<br />


Seulement lors de la compilation j'ai donc des warnings comme quoi il ne trouve pas la définition de la méthode alterCroissance et donc ne sait pas quel type d'objet elle renvois.

Comment faire pour me débarasser de ces warnings ? Ou bien pour faire en sorte que le compilateur connaisse la dite méthode.

Merci d'avance

Réponses

  • BruBru Membre
    20:57 modifié #2
    Essaie de "caster" ton objet (mais je ne sais pas si ça va marcher...)

    <br />NSEnumerator *enumerator = [modifiers objectEnumerator];<br />id modifier;<br />while (modifier = [enumerator nextObject])<br />{<br />&nbsp; if ([modifier respondsToSelector: @selector(alterCroissance)]) c += (int)[(maClasse *)modifier alterCroissance];<br />}
    


    .
  • 20:57 modifié #3
    Ben pour caster mon objet il faudrait que tout les objets dans le NSArray de départ soit du meme type hors justement ce n'est pas le cas puisque l'objectif est de vérifier s'ils répondent bien à  la méthode donnée.
  • Eddy58Eddy58 Membre
    20:57 modifié #4
    Tu as bien fait un #import du fichier d'interface de la classe où se trouve la méthode appelée ?  :o
  • 20:57 modifié #5
    Non, ce qui d'ailleurs m'amène à  une question liée partiellement à  ce problème.

    Comment vous faites quand vous avez deux classes qui doivent s'auto importer ?

    Classe A : #import "ClasseB.h"

    Classe B : #import "ClasseA.h"
  • BruBru Membre
    20:57 modifié #6
    dans 1107297696:

    Ben pour caster mon objet il faudrait que tout les objets dans le NSArray de départ soit du meme type hors justement ce n'est pas le cas puisque l'objectif est de vérifier s'ils répondent bien à  la méthode donnée.


    Dans la mesure où tu testes la présence d'un sélecteur, c'est qu'à  ce moment précis, tu t'attends à  ce que l'objet de ton array soit bien d'une classe précise : le castage peu alors prendre toute son utilité...

    Excepté bien sûr si tu es adepte du polymorphisme (la même méthode implantée de manière identique dans différentes classes sans "ancêtre" commun).

    .
  • BruBru Membre
    20:57 modifié #7
    dans 1107331791:

    Non, ce qui d'ailleurs m'amène à  une question liée partiellement à  ce problème.

    Comment vous faites quand vous avez deux classes qui doivent s'auto importer ?

    Classe A : #import "ClasseB.h"

    Classe B : #import "ClasseA.h"


    Pour résoudre ce problème de d'importation croisée, il faut utiliser le mot-clé @class nom-de-classe;. à  la place du #import.

    Lorsque dans un source, on a juste besoin de connaà®tre un type de classe (et non les méthodes qui y sont associées), il faut utiliser @class.

    .
  • 20:57 modifié #8
    dans 1107333481:

    Dans la mesure où tu testes la présence d'un sélecteur, c'est qu'à  ce moment précis, tu t'attends à  ce que l'objet de ton array soit bien d'une classe précise : le castage peu alors prendre toute son utilité...

    Excepté bien sûr si tu es adepte du polymorphisme (la même méthode implantée de manière identique dans différentes classes sans "ancêtre" commun).


    Dans le cas présent oui je veut faire du polymorphisme je pense. Je vais expliquer un peu mon projet : il s'agit d'un moteur de pbem, jeu de rôle/gestion/diplomatie
    /stratégie tour par tour se jouant en partie par courriel. Chaque joueur est représenté par un objet DEPlayer. Cet objet DEPlayer contient un NSArray d'objets représentant les objets magiques, compétences spéciales, et autres que possèdent où maitrise ce joueur.
    Toutes les méthodes qui s'applique à  ce joueur ou à  ses possessions lors de leur résolution vont tester chaque objet du NSArray pour voir s'il peut influer sur leur résultat.
    L'un des objets pourra influer par exemple sur les croissance de la population de ce joueur, tandis qu'un autre influera sur les capacités de combat.

    Pour le moment aucun de ses objets n'est défini, et je ne compte les créer qu'une fois le jeu lancé au fur et à  mesure des besoins. Mon idée étant à  chaque fois de créer un nouvel objet répondant aux méthodes sur lesquelles il a de l'influence. Cette solution ayant l'avantage de permettre de définir de nouveaux types de bonus plus tard.

    C'est un peu comme l'utilisation d'un delegate, on ne peut pas caster un delegate puisque justement on veut que tout objet puisse l'être avec seulement une vérification ensuite des méthodes auxquelles il répond.
  • ObiObi Membre
    20:57 modifié #9
    dans 1107333662:

    Pour résoudre ce problème de d'importation croisée, il faut utiliser le mot-clé @class nom-de-classe;. à  la place du #import.

    Lorsque dans un source, on a juste besoin de connaà®tre un type de classe (et non les méthodes qui y sont associées), il faut utiliser @class.


    Comment tu utilises @class concretement ?
    J'ai 2 instances de 2 classes différentes qui communiquent entre elles :
    La 1ere utilise "Classe2 *object2;" et "[object2 someAction];" => pas de warning
    La 2eme utilise "id object1;" et "[object1 otherAction];" => warning à  cause du "id"
    Où dois-je mettre le @class ?

    Je recherche cette solution depuis longtemps ! :o
    Je ne savais pas où chercher mais les warnings à  la compilation commencent vraiment à  me pourrir. Faire le tri entre ca et les "vraies" erreurs, ca devient vite fastidieux.
  • fouffouf Membre
    20:57 modifié #10
    Tu déclares le @class dans ton fichier d'interface, après les #imports et avant le @interface . Comme ca pas besoin de faire un import des fichiers que tu utilises dans ton interface.
  • 20:57 modifié #11
    Oui et non. Le fait de mettre une directive @class ne dispense pas d'importer les headers. Au lieu de les importer dans le .h, on les importe dans le .m. Cette directive a pour but d'informer le compilateur de l'existence d'une classe, mais aucunement des méthodes qu'elle peut exécuter (ce qui est le rôle du .h).

    Obi: je t'invite à  regarder ce fil: http://www.macfr.com/forums/index.php?showtopic=16719, tu trouveras une explication bien détaillée de ton problème.
  • AliGatorAliGator Membre, Modérateur
    20:57 modifié #12
    Bonjour à  tous,

    Je réouvre ce vieux thread car finalement j'ai un peu le même souci et j'aimerais bien supprimer ce bête warning qui m'énerve alors que je teste quand même avant pour pas avoir de soucis...

    En l'occurence je veux envoyer un [tt]setEnabled:[/tt] à  tous les objets d'un tableau qui savent y répondre. Or ces objets n'ont pas forcément une classe commune. Du coup malgré un
    if ([obj respondsToSelector:@selector(setEnabled:)]) [obj setEnabled:state];
    
    j'ai le warning alors que, comme je teste, je sais que je n'aurais pas de soucis...

    J'ai bien pensé à  une solution qui consiste à  créer un protocole <CanEnableProtocol> par exemple, contenant cette unique méthode, et indiquer pour toutes mes classes ayant cette méthode qu'elle implémente ce protocole... Du coup j'imagine qu'on pourrait caster l'objet en (CanEnableProtocol) avant d'appeler la méthode dessus (et encore, j'ai jamais fait ça en Obj-C donc j'ai un doute, mais dans les autres langages comme C++ ou Java on peut alors bon...) et le compilo serait content car il sait que setEnabled: est une méthode qui existe si ton objet implémente <CanEnableProtocol>.

    Ca rejoint la solution de caster explicitement l'objet avant d'appeler la méthode, comme proposé par Bru, mais avec les @protocols ça marche aussi même si on a pas un ancêtre commun...
    MAIS quand dans mon tableau y'a aussi des classes Cocoa déjà  existantes, genre NSButton, en plus de classes persos, je peux pas modifier les classes Cocoa pour leur ajouter mon <CanEnableProtocol>, donc cette astuce ne marche plus et tombe à  l'eau...

    Ou alors passer par un pragma qui désactive ce warning juste avant la ligne et le réactive (s'il n'était pas désactivé) juste après ?

    Bon c'est pas dramatique comme problème mais ça fera toujours un warning de moins à  traà®ner (et ça m'empêche si je veux coder propre d'activer l'option de compilo disant "tous es warnings doivent être considérés comme erreurs" en plus :P)
  • CéroceCéroce Membre, Modérateur
    20:57 modifié #13
    Caster en NSControl* dans ce cas ne pose pas problème, puisque justement tu as déjà  vérifié que l'objet répondait à  setEnabled:.
    J'admets ne pas avoir de solution plus généraliste.
  • MalaMala Membre, Modérateur
    novembre 2008 modifié #14
    Je vois deux autres solutions relativement élégantes en fonction du cas rencontré:

    1) utiliser un performSelector... pour lancer la méthode testée avec succès

    2) Déclarer un interface en début du fichier pour la classe ciblée.

    Par exemple, si j'ai besoin d'accéder à  la méthode privée contextID de la classe NSApplication. Au début de mon fichier source je redéclare la méthode comme suit:

    <br />@interface NSApplication (private)<br /><br />- (int)contextID;&nbsp; <br /><br />@end<br />
    


    Et là  le compilateur est content. J'ai une préférence pour la deuxième solution car les méthodes performSelector... sont de mémoire limitées en nombre d'arguments qu'on peut passer à  la méthode appelée. De plus, on évite d'appeler une méthode qui en appelle une autre pour pas grand chose...
  • ChachaChacha Membre
    20:57 modifié #15
    C'est quel warning exactement ? "passing argument of different width due to prototype" ?
  • AliGatorAliGator Membre, Modérateur
    novembre 2008 modifié #16
    dans 1226653862:
    C'est quel warning exactement ? "passing argument of different width due to prototype" ?
    Non c'est "warning: 'NSObject' may not respond to '-setEnabled:'"
    NSArray* myobjects = ...; // tableau d&#39;objets standards Cocoa et de classes persos aussi<br />	for(NSObject* obj in myobjects)<br />	{<br />		if ([obj respondsToSelector:@selector(setEnabled:)])<br />		{<br />			[obj setEnabled:state]; // c&#39;est là  qu&#39;il faudrait trouver une astuce pour le warning<br />		}<br />	}	<br />
    
    C'est vrai que je pourrais caster en (NSControl*) même si c'en est pas un, ou créer un protocole avec la méthode et caster l'objet en ce protocole... même s'il ne l'implémente pas officiellement, juste pour que le compilo soit content et fasse pas de warning.
    Mais bon je trouve pas ça propre non plus de caster dans un type (ou protocole si on peut quoique ça ça serait déjà  mieux) que l'objet en fait n'est pas, genre caster en NSControl* juste parce qu'on sait que la méthode existe dans cette classe et donc que le compilo ne gueulera pas, même si la classe n'a rien à  voir avec NSControl* au final... bêêrk ;)
  • ChachaChacha Membre
    novembre 2008 modifié #17
    dans 1226655606:

    dans 1226653862:
    C'est quel warning exactement ? "passing argument of different width due to prototype" ?

    Non c'est "warning: 'NSObject' may not respond to '-setEnabled:'"

    Ah, ok. J'arrivais pas à  reproduire ton erreur, car je n'utilise jamais NSObject*. Le type "id" est fait pour ça.
    Avec le type "id", le type est un objet indéterminé, et il ne mets pas de warning "may not respond". ça ne te plaà®t pas comme technique ?
    for(id object in ...)
    


    Sinon, dans l'idée, il faudrait comme dit Mala effectuer un performSelector.
    Malheureusement, performSelector ne fait pas de coercition, et on ne peut y passer un bool comme paramètre.

    <br />&nbsp; if ([obj respondsToSelector:@selector(setEnabled:)])<br />&nbsp; &nbsp; [obj performSelector:@selector(setEnabled:) withObject:[NSNumber numberWithBool:YES]]; //ben non<br />
    


    La solution serait alors NSInvocation, mais c'est bien lourdingue...

    Reste cette petite bricole, en utilisant directement le type IMP !

    <br />&nbsp; if ([obj respondsToSelector:@selector(setEnabled:)])<br />&nbsp; &nbsp; [obj methodForSelector:@selector(setEnabled:)](obj, @selector(setEnabled:), YES, 0); //ne pas oublier le 0 final pour spécifier la fin des paramètres<br />
    


    ou alors avec cast explicite du prototype

    <br />&nbsp; if ([obj respondsToSelector:@selector(setEnabled:)])<br />&nbsp; &nbsp; ((void (*)(id, SEL, BOOL))[obj methodForSelector:@selector(setEnabled:)])(obj, @selector(setEnabled:), NO);<br />
    
  • schlumschlum Membre
    20:57 modifié #18
    C'est hors-sujet au vu du titre, mais ça concerne ton histoire de validation ou non de contrôles...
    Pourquoi ne pas utiliser l'interface NSUserInterfaceValidation faite pour ça ?

    http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSUserInterfaceValidations_Protocol/Reference/Reference.html
  • AliGatorAliGator Membre, Modérateur
    20:57 modifié #19
    dans 1226656379:

    Ah, ok. J'arrivais pas à  reproduire ton erreur, car je n'utilise jamais NSObject*. Le type "id" est fait pour ça.
    Avec le type "id", le type est un objet indéterminé, et il ne mets pas de warning "may not respond". ça ne te plaà®t pas comme technique ?
    for(id object in ...)
    
    Ah ben si, carrément...
    En fait je sais pas pourquoi j'ai pas utilisé le type "id"... ou plutôt si, je sais, c'est parce que je me disais que pour appeler respondsToSelector fallait que ce soit un NSObject car respondsToSelector est une méthode de NSObject... mais vu que ça peut s'appeler sur un id aussi puisque c'est de l'envoi de message...

    Bon par contre je suis étonné d'un truc : je me suis dit "bon du coup si mon objet est de type id, du coup il va jamais me mettre de warning si j'appelle une méthode dessus même si elle n'existe pas... donc si je fais une faute de frappe sur respondsToSelector, il me la mentionnera pas pour le coup, c'est dommage...
    Ben en fait si, si je mets "respondsToZelectorq" genre à  la place, il me met un warning comme quoi il connaà®t pas cette méthode
    warning: no '-respondsToZelectorq:' method found
    (notez c'est pas le même warning que le problème d'avant)... d'ailleurs si j'appelle la méthode setEnabledZ au lieu de etEnabled, j'ai aussi le même warning sur la ligne correspondante !
    Ca veut dire qu'il sait constamment toutes les méthodes qui existent dans toutes les classes quelles qu'elles soient, pour savoir que le sélecteur n'existe nulle part ? Trop fort le gcc...

    dans 1226656379:
    Sinon, dans l'idée, il faudrait comme dit Mala effectuer un performSelector.
    Malheureusement, performSelector ne fait pas de coercition, et on ne peut y passer un bool comme paramètre.
    Ben figure toi que j'avais essayé, avant même que Mala le propose d'ailleurs, ça faisait partie de mes tests pour contourner le warning justement... et j'en suis arrivé au même problème que toi :P

    dans 1226656379:
    La solution serait alors NSInvocation, mais c'est bien lourdingue...
    Heu ouais faut pas abuser non plus :D J'aimerais bien enlever ce warning (d'ailleurs avec la solution du "id" maintenant, c'est fait, merci) mais pas à  ce point quand même :)

    dans 1226656379:
    Reste cette petite bricole, en utilisant directement le type IMP !
    Pareil, j'y ai pensé mais ça me plait moyen côté syntaxe aussi (et à  tous les coups je l'aurais oublié, le zéro/NULL de fin ;))


    Bref y'a des fois où j'ai encore du mal à  voir à  quoi correspond ce type magique id côté concept, mais pour le coup j'aurais dû y penser... Merci ;)
Connectez-vous ou Inscrivez-vous pour répondre.