Property et id : un comportement restrictif pour id

Philippe49Philippe49 Membre
mai 2009 modifié dans API UIKit #1
Le code suivant provoque une erreur désagréable à  la compilation (ci-dessous)

-(IBAction) moveButton:(id)sender {
if(sender.enabled) NSLog(@enabled);


Les seules solutions que j'ai trouvées c'est soit le cast, lourdingue si il faut le faire beaucoup de fois, ou l'utilisation d'une variable auxiliaire :
-(IBAction) moveButton:(id)sender {
if(((UIControl*)sender).enabled) NSLog(@enabled);

-(IBAction) moveButton:(id)sender {
UIButton * theButton=sender;
if(theButton.enabled) NSLog(@enabled);

On peut aussi évidemment ne pas utiliser les properties ... [sender isEnabled], mais c'est dommage, notamment parce que cela peut déclencher des animations automatiques.

Qu'en pensez-vous ?

Réponses

  • CéroceCéroce Membre, Modérateur
    01:09 modifié #2
    As-tu essayé

    if([sender valueForKeyPath:@"enabled"])
    


    Après tout, les properties sont bien traduites en Key-Value Coding, si j'ai bien compris (désolé, mais je ne programme pas en ObjC 2.0).
  • Philippe49Philippe49 Membre
    01:09 modifié #3
    dans 1226753388:

    if([sender valueForKeyPath:@"enabled"])
    


    Oui, cela marche aussi .. comme [sender isEnabled] qui lui est équivalent.
  • AliGatorAliGator Membre, Modérateur
    01:09 modifié #4
    En fait ce n'est pas valable que pour "id".
    Quand on utilise la syntaxe pointée, il faut que l'objet sur lequel on l'utilise soit exactement de la classe qui contient la propriété, ou d'une sous-classe.

    Ainsi, soit par exemple un MyNavigationController dérivant de UINavigationController et auquel on a rajouté une [tt]@property BOOL toto[/tt].
    Si dans le code d'une UIViewController quelconque on demande [tt]BOOL b = self.navigationController.toto[/tt] alors là  aussi on va avoir la même erreur que décrite, car "self.navigationController" va retourner un UINavigationController (générique) et pas un MyNavigationController.

    Alors qu'en utilisant la syntaxe d'envoi de message [tt]BOOL b = [self.navigationController isToto];[/tt] là  ça va marcher, car ce n'est pas à  la compilation mais à  l'exécution que va se faire la résolution...
  • Philippe49Philippe49 Membre
    01:09 modifié #5
    dans 1228100761:

    car ce n'est pas à  la compilation mais à  l'exécution que va se faire la résolution...

    Ben oui, j'avais bien compris cela, et ce que je me demande c'est pourquoi les properties ne réagiraient pas de même, car là  Objective-C semble délaisser un mode de fonctionnement bien pratique : le typage dynamique des messages. Je dis semble car comme cette situation est frappée d'une erreur et non d'un warning, on ne peut pas essayer.
  • ChachaChacha Membre
    décembre 2008 modifié #6
    La restriction est au moins due à  cela :
    Supposons une classe A avec une propriété toto.
    A* a = ...
    id b = a;
    NSLog(@%@, b.toto); //erreur de compilation !

    erreur pourquoi ? Car les propriétés doivent être aussi rapides que les appels de méthodes. Donc l'appel b.toto doit être résolu à  la compilation, c'est-à -dire que le compilateur doit déterminer quel message envoyer pour b.toto.
    La logique voudrait que ce soit [b toto].
    Sauf que le getter associé à  la propriété toto dans la classe A pourrait être getToto, ou getBlug, que la donnée d'instance pourrait s'appeler "bibifoc" et non toto... bref, si on ne connaà®t pas la classe sous-jacente, on ne peut pas résoudre la syntaxe "b.toto".
    Une propriété ne passe pas par du KVC (key-value coding), elle a été conçue pour avoir la rapidité des appels de méthodes auxquelles elle correspond.

    +
    Chacha
  • Philippe49Philippe49 Membre
    01:09 modifié #7
    dans 1228118301:

    La logique voudrait que ce soit [b toto].
    Sauf que le getter associé à  la propriété toto dans la classe A pourrait être getToto, ou getBlug, que la donnée d'instance pourrait s'appeler "bibifoc" et non toto... bref, si on ne connaà®t pas la classe sous-jacente, on ne peut pas résoudre la syntaxe "b.toto".

    Justement quel est le véritable intérêt de cette liberté de choix du getter et du setter pour une property ? Tu as eu l'occasion de rencontrer une situation où cela simplifie quoi que ce soit? Est-ce que cela vaut le coup de perdre le typage dynamique ? est-ce un bon choix qui a été fait ici ?

    dans 1228118301:

    Car les propriétés doivent être aussi rapides que les appels de méthodes.

    Cela resterait possible.

  • ChachaChacha Membre
    01:09 modifié #8
    dans 1228120651:

    Justement quel est le véritable intérêt de cette liberté de choix du getter et du setter pour une property ?
    Tu as eu l'occasion de rencontrer une situation où cela simplifie quoi que ce soit?

    Si ta variable est un booléen "toto", tu peux préferer que ton getter soit "isToto" plutôt que "toto".
    Si ta politique de nommage de variable dit que tout membre interne doit commencer par un "_", ta donnée s'appelle"_toto", mais tu préfères que ton getter s'appelle "toto".


    dans 1228118301:

    Car les propriétés doivent être aussi rapides que les appels de méthodes.

    Cela resterait possible.

    Comment ? S'il faut au run-time faire une introspection, autant passer par du KVC...

    +
    Chacha
  • Philippe49Philippe49 Membre
    01:09 modifié #9
    Cela veut bien dire que les properties ne sont pas compatibles avec id et ainsi chaque fois qu'on fait une IBAction avec un argument (id) sender , on doit faire une gymnastique ... que l'on n'a pas forcément à  faire autrement !

    dans 1228122665:

    Si ta variable est un booléen "toto", tu peux préferer que ton getter soit "isToto" plutôt que "toto".
    Si ta politique de nommage de variable dit que tout membre interne doit commencer par un "_", ta donnée s'appelle"_toto", mais tu préfères que ton getter s'appelle "toto".

    Cela c'est possible
    You can use the form property=ivar to indicate that a particular instance variable should be used for the property, for example:
    @synthesize firstName, lastName, age = yearsOld;




    dans 1228122665:

    Comment ? S'il faut au run-time faire une introspection, autant passer par du KVC...

    En faisant le choix de standardiser les getter et les setter, plutôt que celui de s'interdire qu'une action puisse être appelée par des control de classes différentes.
  • ChachaChacha Membre
    01:09 modifié #10

    Cela c'est possible
    You can use the form property=ivar to indicate that a particular instance variable should be used for the property, for example:
    @synthesize firstName, lastName, age = yearsOld;


    Oui, ça gère le cas _toto, pas isToto. Et pour le cas _toto, ça n'est pas forcément une bonne idée (tout dépend du point de vue), puisque cette partie se trouve alors dans @implementation et non plus dans @interface; c'est donc moins bien auto-documenté.

    dans 1228122665:

    En faisant le choix de standardiser les getter et les setter, plutôt que celui de s'interdire qu'une action puisse être appelée par des control de classes différentes.

    Apple a fait le choix de laisser les getters et setters libres; il y a des avantages et des inconvénients. Autre avantage que l'on pourrait citer : un développeur peut faire du refactoring rapide, et changer les méthodes sous-jacentes à  une propriété sans remettre en cause le code utilisant la propriété.

  • AliGatorAliGator Membre, Modérateur
    01:09 modifié #11
    En lisant la question de notre grand chef (tiens je sais plus son nouveau nom  il change tellement souvent et en plus pour des trucs imprononçables :o ), j'ai relu vite-fait cette discussion...
    Et me suis fait la réflexion : pourquoi laisser le typage en (id) Philippe qd tu sais de quel type il va s'agir ?

    Je veux dire en fait les IBActions ce ne sont que des messages envoyés à  ton objet. La signature type est -(IBAction)monAction:(id)sender, mais :
    1) Sous iPhone OS, la syntaxe sans paramètre (et donc sans passer le sender) est aussi acceptée, ainsi qu'une syntaxe contenant le "controlEvent" associé
    2) c'est un envoi de message dynamique

    Donc si tu signes ton IBAction ainsi, ça devrait passer aussi, non ?
    (IBAction) moveButton:(UIButton*)sender {<br />&nbsp;  if(sender.enabled) NSLog(@&quot;enabled&quot;);<br />}
    
    Ainsi tu précises directement que ton sender est sensé être un UIButton (bon bien sûr si c'est pas le cas tu auras des problèmes... mais au même titre que ta solution de passer par une variable intermédiaire... ainsi qu'au même titre que si tu essayes d'accéder à  une clé par du KVC alors qu'elle n'est pas définie... donc au final ça pose pas de problème), ça équivaut à  la solution de passer par une variable intermédiaire... mais sans variable intermédiaire, en typant directement le paramètre ;)

    Après faut voir si IB reconnaà®t bien l'IBAction si elle est définie ainsi et pas avec un (id) par contre, j'ai pas testé, ça.
  • Philippe49Philippe49 Membre
    01:09 modifié #12
    dans 1241440325:

    Et me suis fait la réflexion : pourquoi laisser le typage en (id) Philippe qd tu sais de quel type il va s'agir ?

    C'est vrai. La force de l'habitude

    dans 1241440325:

    1) Sous iPhone OS, la syntaxe sans paramètre (et donc sans passer le sender) est aussi acceptée, ainsi qu'une syntaxe contenant le "controlEvent" associé

    J'avais pas remarqué. Tu as la référence de la doc associée ? Cela peut-être utile de récupérer le controlEvent.
  • AliGatorAliGator Membre, Modérateur
    01:09 modifié #13
    J'avais pas remarqué. Tu as la référence de la doc associée ? Cela peut-être utile de récupérer le controlEvent.
    UIControl Class Reference : The Target/Action mechanism
    UIKit allows three different forms of action selector:

    - (void)action
    - (void)action:(id)sender
    - (void)action:(id)sender forEvent:(UIEvent *)event
    The sendAction:to:fromSender:forEvent: method of UIApplication pushes two parameters when calling the target. These last two parameters are optional for the application because it's up to the caller (usually a UIControl object) to remove any parameters it added.
  • Philippe49Philippe49 Membre
    01:09 modifié #14
    dans 1241440325:

    Après faut voir si IB reconnaà®t bien l'IBAction si elle est définie ainsi et pas avec un (id) par contre, j'ai pas testé, ça.

    Merci de ces précisions. 
    J'ai testé avec IB, tout baigne !
  • FloFlo Membre
    01:09 modifié #15
    Est-ce que cette solution marcherait pour les applis mac ?

    Je suis moi-même embêté par ce genre de problème...
Connectez-vous ou Inscrivez-vous pour répondre.