Property et id : un comportement restrictif pour id
Philippe49
Membre
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 ?
-(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 ?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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).
Oui, cela marche aussi .. comme [sender isEnabled] qui lui est équivalent.
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...
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.
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
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 ?
Cela resterait possible.
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".
Comment ? S'il faut au run-time faire une introspection, autant passer par du KVC...
+
Chacha
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;
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.
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é.
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é.
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 ? 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.
C'est vrai. La force de l'habitude
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.
Merci de ces précisions.
J'ai testé avec IB, tout baigne !
Je suis moi-même embêté par ce genre de problème...