Philosophie programmation et efficacité

RocouRocou Membre
04:57 modifié dans API AppKit #1
Bonjour,

J'ai une question triviale concernant la façon la plus propre de programmer en Objective_C/Cocoa.

Pour un développeur qui vient du monde C, faut-il remplacer les fonctions C par des classes où peut-on conserver la notion de fonctions?

Par exemple, dans mon application actuelle, j'initialise le contenu des objets de mon interface (NSPopupButton, NSTableView, etc.) dans la partie 'AwakeFromNib'. Il y a un bout de code par objet, ce qui, quand l'interface est chargée, devient lourdingue...

Réponses

  • schlumschlum Membre
    04:57 modifié #2
    ça dépend quelles fonctions... Quand j'ai des traitements lourds backtracking, récursion... je fais ça en C.
  • Nebuchad34Nebuchad34 Membre
    04:57 modifié #3
    On le répète souvent sur ce Forum : Pour appréhender la programmation en Cocoa et adopter les conventions usuelles, rien de mieux que le bouquin de Aaron Hillegass "Programmation Cocoa sous Mac OS X". C'est une référence à  lire avant de commencer tout programme en Cocoa car il est permet de partir sur de bonnes bases en s'exerçant sur des programmes simples.

    Etant moi-même débutant en Cocoa, je ne saurais que trop te conseiller de lire ce livre avant de commencer à  coder, cela évitera de prendre de mauvaises habitudes.

    Voilà   ;)
  • AliGatorAliGator Membre, Modérateur
    04:57 modifié #4
    Alors il ne faut pas garder la notion de fonctions mais de méthodes.
    En fait c'est à  peu près le même principe pour les deux, mais une méthode est en quelques sortes une fonction que tu appelles sur un objet. C'est plus courant dans les langages orientés objet (par exemple tu demandes à  ton objet "Voiture" de démarrer, plutôt que d'appeler une fonction demarrerLaVoiture(...))

    Après bien sûr il est préférable de décomposer en effet. Pour reprendre ton exemple d'initialisation de ton interface, tu peux créer des méthodes dans le AppController où tu as mis ton awakeFromNib, par exemple une méthode "[tt]-(void)setContentsOfPopupButton:(NSPopupButton*)btn;[/tt]" que tu déclares dans ton AppController. Et du coup tout le code qui te sert à  initialiser ton PopupButton, tu le déplaces de awakeFromNib vers cette méthode, et à  la place dans awakeFromNib tu appelles la méthode via un [tt][self setContentsOfPopupButton:popupBtn];[/tt] (où popupBtn serait IBOutlet vers ton popupbutton bien sûr)

    Voilà  pour l'idée ;)
  • NoNo Membre
    04:57 modifié #5
    dans 1239700516:

    Par exemple, dans mon application actuelle, j'initialise le contenu des objets de mon interface (NSPopupButton, NSTableView, etc.) dans la partie 'AwakeFromNib'. Il y a un bout de code par objet, ce qui, quand l'interface est chargée, devient lourdingue...


    C'est le cas typique de l'utilisation d'une fonction C au sein d'un code Obj-C par exemple.
    Une petite fonction C pour regrouper le code d'initialisation d'un objet UI, et juste son appel dans le awakeFromNib est une solution viable, et effectivement, permet d'alléger le code en amont.

    Bien sûr, comme dit Ali, la fonction peut être remplacée par une méthode spécifique et appelée via self.
    mais bon, utiliser le mécanisme message/objet pour un bout de code privé est peut-être plus lourdingue que le simple emploi d'une bonne vieille fonction C.
  • AliGatorAliGator Membre, Modérateur
    04:57 modifié #6
    Oui, ça se discute.
    Perso côté pédagogique je préfère orienter l'architecture de l'application en tout-objet, et donc habituder Rocou à  la POO plutôt que de le faire trop se rattacher à  ses principes du C où la notion d'objet est totalement absente... Ca permet donc en plus d'appréhender mieux la POO, et côté propreté je trouve ça plus clean... mais bon c'est un avis perso.

    En effet rien ne t'empêche d'utiliser, en particulier dans ce cas, des fonctions C au lieu de méthodes Objective-C... ça fait moins POO mais ça marche aussi... mais bon perso moi je ne garde l'utilisation des fonctions C que pour les parties algorithmiques, genre calculs intensifs ;)
  • schlumschlum Membre
    04:57 modifié #7
    Quand la méthode n'a besoin pas besoin d'accéder aux i-vars, pas besoin que ça soit une méthode d'instance (en fait c'est même plutôt illogique que ça en soit une).
    Si c'est vraiment lié à  la classe, on peut en faire une méthode de classe... sinon, si c'est plutôt générique (comme NSMakePoint par exemple), une fonction C est plus adéquate.
  • RocouRocou Membre
    04:57 modifié #8
    Merci à  tous. J'aime bien la façon de faire conseillée par Aligator.
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #9
    Après, il ne faut pas oublier qu'une méthode ObjC c'est une fonction C qui prend deux paramètres cachés (id self et SEL _cmd qui représente respectivement le receveur du message et le nom du message).

    Sachant cela, il vous est possible de créer des "méthodes" privées pures, car, comme vous le savez/devriez le savoir aucune méthode n'est totalement privée en ObjC, il suffit de connaà®tre son nom pour pouvoir l'appeler, mais avec une fonction C, il vous est possible d'accéder aux ivars de votre objet tout en garantissant que cette fonction ne sera pas appelée par une personne malveillante :

    // .h<br />@interface MyClass : NSObject {<br />&nbsp; &nbsp; int ivar;<br />}<br />@end<br /><br />// .m<br />@implementation MyClass<br />static void privateMethod(MyClass *self, int value)<br />{<br />&nbsp; &nbsp; // Même protégée, cette variable est accessible directement<br />&nbsp; &nbsp; // puisque la fonction est définie dans le @implementation de la classe<br />&nbsp; &nbsp; self-&gt;ivar = value;<br />}<br /><br />- (id)init<br />{<br />&nbsp; &nbsp; if(self = [super init])<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; privateMethod(self, 10);<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}<br /><br />@end
    


    Bien sûr, ici c'est pas un super exemple, un setter privé n'est pas le genre de trucs qu'on utilise souvent, mais ce genre de technique peut être utile dans d'autres cas, par exemple dans le cas d'une fonction qu'on utilise intensément, ou dans le cas de code qu'il ne faut pas dévoiler.
  • schlumschlum Membre
    04:57 modifié #10
    Dans ce genre de contournements tordus, on se demande pourquoi faire de l'Objective-C plutôt que du C++...

    C'est presque comme le coup du hack des instances Obj-C sur la pile  :)


    Quitte à  faire une fonction C, autant lui passer les in/out de manière classique et non l'objet en lui même...
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #11
    Je vois pas en quoi cette technique est un hack, de plus si tu veux pouvoir envoyer des messages à  l'objet, c'est plus simple si tu as l'adresse de l'objet directement...
  • AliGatorAliGator Membre, Modérateur
    04:57 modifié #12
    Beuurk
  • schlumschlum Membre
    04:57 modifié #13
    dans 1241855637:

    Je vois pas en quoi cette technique est un hack, de plus si tu veux pouvoir envoyer des messages à  l'objet, c'est plus simple si tu as l'adresse de l'objet directement...


    Tout simplement parce que c'est anti-POO  ;)
    Depuis quand une fonction C fait partie des méthodes de l'objet en Objective-C, quand bien même elle est dans le .m ?  B)
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #14
    dans 1241988928:

    dans 1241855637:

    Je vois pas en quoi cette technique est un hack, de plus si tu veux pouvoir envoyer des messages à  l'objet, c'est plus simple si tu as l'adresse de l'objet directement...


    Tout simplement parce que c'est anti-POO  ;)
    Depuis quand une fonction C fait partie des méthodes de l'objet en Objective-C, quand bien même elle est dans le .m ?   B)


    Depuis que les méthodes ObjC sont des fonctions C avec des paramètres cachés, et le simple fait de les mettre dans le @implementation te permet de les faire fonctionner de la même manière. Et si tu veux tu peux même rajouter la méthode au runtime...
  • schlumschlum Membre
    04:57 modifié #15
    Tu as dit le mot... " cachés "  ;)
    Ce que tu essaies de faire, c'est de by-passer les faiblesses du langage par des techniques extérieures à  ce langage.
    Ce qui s'appelle... un hack.
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #16
    Elles ne sont pas extérieurs au langage, tout est parfaitement dans les règles... Tu ne peux pas prétendre que le C est extérieur à  l'ObjC, alors qu'ObjC est fait à  80% de C plus quelques fioritures...
  • schlumschlum Membre
    04:57 modifié #17
    C'est pas le C le problème  :o
    C'est que tu accèdes à  des membres d'une classe dans une fonction qui n'est pas une méthode de cette classe... C'est ça qui est complètement anti-POO !

    Si un jour le fonctionnement interne de l'Objective-C change pour mieux coller à  l'encapsulation, ton code ne compilera plus... là  est la limite de la définition du hack  ;)
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #18
    Bah non c'est pas anti-POO puisque la fonction est dans la même portée que les méthodes de la classe, elle en a donc le plein accès.

    Si, cette technique est un hack, alors les catégories le sont aussi...
  • schlumschlum Membre
    mai 2009 modifié #19
    Du point de vue du langage, ta fonction C n'est pas une méthode de la classe (même si ça l'est du point de vue de l'implémentation actuelle du runtime)...
    Dans la définition de l'Objective-C, il n'est pas écrit qu'une fonction C dans le .m peut accéder aux propriétés privées de la classe. C'est par ailleurs quelque chose qui peut changer dans une future évolution du langage.

    Imaginons un Objective-C 3.0 qui implémente les méthodes privées et interdise à  la compilation ce que tu fais en renforçant l'encapsulation (pourrie actuellement) du langage... Ton code se retrouve alors caduque et tu vas devoir reprendre des centaines / milliers de lignes de code.
    Alors qu'un code sans hacks de ce genre, il aura beau être écrit en Obj-C 1.0, il sera compilable avec le 2.0, le 3.0, le 4.0... car la compatibilité descendante est assurée à  ce niveau.

    Quant aux catégories, je ne vois pas le rapport, ça fait partie du langage  ???
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #20
    dans 1241993675:

    Du point de vue du langage, ta fonction C n'est pas une méthode de la classe (même si ça l'est du point de vue de l'implémentation actuelle du runtime)...


    Justement non, les fonctions C ne sont pas des méthodes de la classe du point de vue du runtime, elles le sont du point de vue du compilateur.

    Pour commencer, avant ObjC 2, tu pouvais accéder aux variables d'instance d'une classe à  l'extérieur de celle-ci même si les ivars étaient @private ou @protected, tu avais cependant un avertissement de la part du compilateur te disant que cet accès pourrait ne plus marcher dans de futures implémentations...

    Voilà  qui est maintenant corrigé.

    Cependant, cet avertissement n'a jamais existé et n'existe toujours pas quand tu utilises une ivar dans une fonction C à  l'intérieur de @implementation, et c'est pas une violation d'encapsulation, non, c'est tout simplement parce que ObjC fonctionne comme cela, les méthodes sont simplement des fonctions static, c'est-à -dire emprisonnées dans leur unité de compilation, dont on récupère l'adresse et qu'on met dans une structure adaptée.
    La différence entre la fonction C et la méthode c'est que la première ne sera pas enregistrée par le runtime, mais l'une comme l'autre ont accès aux ivars de self et des autres objets de la même classe.

    Et je peux te garantir que ça ne changera pas avec d'autres version d'ObjC, car il s'agit des fondements, et d'ailleurs, si cela allait être changé, ils auraient mis des avertissements. :P
  • schlumschlum Membre
    04:57 modifié #21
    Tu arranges le langage à  ta sauce là   ;)
    Montre moi une documentation officielle d'Objective-C, ou même un livre sur le langage disant que les fonctions C définies à  l'intérieur de @implementation ont droit d'accéder aux i-var de la classe...
    Si tu la trouves, je suis convaincu d'emblée (même si je ne l'utiliserai pas, trouvant ça quand même crado  :) ) !

    L'avertissement pour les accès extérieur, c'est parce que c'était tellement gros et tellement violent qu'ils se sont sentis obligés de le mettre ; ils n'avaient juste pas eu le temps de le fixer pour la 1.0 c'est tout  :P
    Il n'y a pas d'avertissements pour tout un tas de choses... à  commencer par les méthodes non documentées.
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #22
    Ha si, il y a un avertissement pour les méthodes non-documentées, en tout cas celles qui ne sont pas définies dans un @interface accessible, tu as un warning qui te dit que l'objet ne répond peut-être pas au message que tu lui donnes.
  • schlumschlum Membre
    mai 2009 modifié #23
    Ben... c'est pas un avertissement disant " attention, cet appel risque de ne plus fonctionner à  la prochaine mise à  jour du SDK "  :)

    Ils vont pas non plus tenir la main aux développeur pour tout... Chacun prend ses responsabilités  :P
    Personnellement, je ne prendrais pas ce risque et je ne le conseillerais à  personne car ce n'est pas écrit dans la définition du langage, c'est tout...  ;)

    De même que l'utilisation directe de "objc_msgSend" et tout un tas d'autres choses hein, c'est pas péjoratif !  :P
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #24
    Ha j't'arrête tout d'suite, objc_msgSend() est parfaitement documenté et ils te donnent la technique te permettant d'envoyer un message avec cette fonction directement.

    Bon, bien sûr ils ne conseillent pas au commun des mortels de l'utiliser directement, cependant ils montrent comment l'utiliser pour ceux qui veulent implémenter un bridge par exemple.

    If your application directly calls the Objective-C runtime function objc_msgSend, you should always cast to the appropriate return value. For instance, for a method that returns a BOOL data type, the following code executes properly on a PPC Macintosh but might not on an Intel-based Macintosh computer:

    BOOL isEqual = objc_msgSend(string, @selector(&quot;isEqual:&quot;), otherString);
    

    To ensure that the code does executes properly on an Intel-based Macintosh computer, you would change the code to the following:

    BOOL isEqual = ((BOOL (*)(id, SEL, id))objc_msgSend)(object, @selector(&quot;isEqual:&quot;), otherString);
    

  • schlumschlum Membre
    mai 2009 modifié #25
    Exact...
    ocrtHowMessagingWorks

    C'est nouveau ? Il me semblait avoir galéré pour trouver de la documentation dessus il y a quelques années  ???
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #26
    Je pensais surtout à  ce document: Objective-C Runtime: Sending Messages

    Et sinon je pourrais pas te dire si c'est si récent que ça, mais il est vrai que je n'ai découvert ça qu'assez récemment.
  • schlumschlum Membre
    04:57 modifié #27
    La page que j'ai donnée c'est carrément une section de l' " Objective-C 2.0 Runtime Programming Guide " (corrigé le lien qui était vérolé...)
    Y a pas mal d'explications, c'est bien foutu... Obj-C 2.0 nous a au moins apporté une doc correcte sur certains recoins obscurs du langage  :P
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #28
    Ouaip et Clang, m'a aussi beaucoup aidé à  comprendre comment fonctionnait le runtime... :D
  • AliGatorAliGator Membre, Modérateur
    04:57 modifié #29
    Je suis plutôt d'accord avec schlum sur le coup. Ce n'est pas parce que le langage te permet un truc, surtout si c'est alambiqué et pas documenté dans les principes de base, que c'est "propre" de le faire.
    D'une part parce que l'Objective-C est basé sur le C, mais son but est de rajouter une couche objet au langage C qui n'en n'a pas à  la base. Si tu détournes le langage Objective-C pour faire du "C objet" à  ta sauce (en utilisant éventuellement avec des moyens détournés les mêmes techniques que l'Objective-C utilise sous le capot, ce qui n'est pas dit), rien ne garantit la compatibilité en cas d'évolution. Certes c'est faisable... mais à  quoi bon, surtout quand ça complique la lecture du programme et n'a aucune garantie de pérennité...

    C'est comme si, en C, tu écrivais des trucs genre :
    void toto(int a)<br />{<br />&nbsp; void* stk = (void*)(&amp;a-8);<br />&nbsp; ...<br />}
    
    Histoire d'accéder à  la pile de façon détournée... (alors qu'en plus selon les compilateurs et si c'est du __stdcall ou autre, la pile ne sera pas ordonnée pareil...), ou comme si, je sais pas moi, dans un langage objet tu n'utilisais pas d'accesseurs pour tes variables alors que la POO le préconise, ...

    Si on creuse bien, on peut tout faire... je dirais même on peut faire tout et n'importe quoi. En tiraillant le langage dans tous les sens, y'a toujours moyen de faire des trucs... mais à  la base c'est pas prévu pour, c'est pas pensé pour.
    Tu peux conduire ta voiture en marche arrière pendant tout un trajet si ça te chante, mais bon, c'est pas prévu pour quoi...
  • psychoh13psychoh13 Mothership Developer Membre
    04:57 modifié #30
    Bah si tu veux faire un truc qui est prévu par le langage (le runtime en fait) mais qui en revanche est bien dégueulasse, tu peux t'amuser à  utiliser ivar_getOffset() pour accéder aux variables d'instance que tu veux... :D
Connectez-vous ou Inscrivez-vous pour répondre.