Key-Value Coding : Danger ?

segaoufsegaouf Membre
mai 2010 modifié dans Objective-C, Swift, C, C++ #1
Bonjour a tous,

Je continue dans mon apprentissage du developpment cocoa. Hier soir j'ai appris l'existance du KVC. Mais ce concept est flippant, non ?  :o

Il me semble que les variables d'instances sont prives par defaut dans objective-c, pas sur, mais si c'est vrai je trouve cela bien.

Par contre, avec KVC, on fait sauter cette protection, non ?

Ce qui me bloque, c'est qu'il me semble qu'avec le KVC on peut recuperer les variables d'un objet et les modifier, meme si le createur de la classe voulait nous forcer a passer par un setter ?

C'est pas dangereux ? Quel en est l'interet ?

Merci :)




Réponses

  • AliGatorAliGator Membre, Modérateur
    mai 2010 modifié #2
    En effet il faut faire gaffe.

    Mais de toute façon le KVC passe par les setters et les getters s'ils existent. Donc si le concepteur de la classe a prévu un getter et un setter, ils seront utilisés quand tu utilises le KVC (ce qui permet de s'assurer du respect des règles de gestion mémoire retain/release, entre autres, quand tu utilises les setters).
    Donc [tt][obj setValue:x forKeyPath:@a.b][/tt] sera équivalent à  [tt][[obj valueForKey:@a] setValue:x forKey:@B][/tt] qui sera traduit par [tt][[obj a] setB:x][/tt], donc le setter setB sera bien appelé pour modifier la valeur de b.


    Pour ce qui est de l'accès aux variables privées, il y a une protection. En effet, si tu regardes bien ce qui est indiqué ici dans la doc, tu verras que le KVC n'utilise en fait que les setters/getters (il faut donc qu'ils existent), et ne s'autorise à  accéder directement aux variables (s'il ne trouve pas de setter/getter) que si [tt]accessInstanceVariablesDirectly[/tt] retourne YES.
    Bon malheureusement (quoique ça se discute justement, ça a ses avantages, mais comporte aussi les risques que tu as soulevé concernant les variables privées du coup), l'implémentation par défaut de cette méthode (du protocole <NSKeyValueCoding>) retourne justement YES. Mais il suffit de la surcharger dans tes classes pour lui faire retourner NO et protéger ainsi tes variables privées y compris à  l'égard du KVC.
  • segaoufsegaouf Membre
    mai 2010 modifié #3
    dans 1273595217:

    En effet il faut faire gaffe.

    Mais de toute façon le KVC passe par les setters et les getters s'ils existent. Donc si le concepteur de la classe a prévu un getter et un setter, ils seront utilisés quand tu utilises le KVC (ce qui permet de s'assurer du respect des règles de gestion mémoire retain/release, entre autres, quand tu utilises les setters).
    Donc [tt][obj setValue:x forKeyPath:@a.b][/tt] sera équivalent à  [tt][[obj valueForKey:@a] setValue:x forKey:@B][/tt] qui sera traduit par [tt][[obj a] setB:x][/tt], donc le setter setB sera bien appelé pour modifier la valeur de b.


    Pour ce qui est de l'accès aux variables privées, il y a une protection. En effet, si tu regardes bien ce qui est indiqué ici dans la doc, tu verras que le KVC n'utilise en fait que les setters/getters (il faut donc qu'ils existent), et ne s'autorise à  accéder directement aux variables (s'il ne trouve pas de setter/getter) que si [tt]accessInstanceVariablesDirectly[/tt] retourne YES.
    Bon malheureusement (quoique ça se discute justement, ça a ses avantages, mais comporte aussi les risques que tu as soulevé concernant les variables privées du coup), l'implémentation par défaut de cette méthode (du protocole <NSKeyValueCoding>) retourne justement YES. Mais il suffit de la surcharger dans tes classes pour lui faire retourner NO et protéger ainsi tes variables privées y compris à  l'égard du KVC.


    Tres interessant (Comme d'hab de ta part quoi.)

    J'etais plus au moins vaguement au courant  pour la accessInstanceVariablesDirectly, j'avais lu ceci quelque part sur stackoverflow. Mais le fait qu'elle retourne YES par defaut la rend un peu inutile ...

    J'ai aussi plus ou moins compris qu'il cherche le setter/getter en premier lieu. Mais Imaginons que je ne veux pas qu'il soit possible de setter une variable, je vais devoir creer un setter qui ne fait rien ! Tres mauvaise pratique, et cela peut rendre un developpeur completement maso en pensant que le setter est un vrai setter ...

    Ou overrider accessInstanceVariablesDirectly ... Mais dans ce cas, on perd la finesse du public/privee, vu que toutes les variables dans cette classe deviendront accessible/inaccessible, non ?


  • AliGatorAliGator Membre, Modérateur
    mai 2010 modifié #4
    Qui utilise des variables publiques de toute façon, en Objective-C ? (d'ailleurs, dans d'autres langages, c'est également déconseillé)
    La bonne pratique veut qu'on utilise tous des getters/setters. Si tu veux une variable read-only, elle aura un getter mais pas de setter.
    C'est ce qui se passe quand tu déclares @property(readonly) d'ailleurs, ça ne déclare que le getter (et le @synthesize n'implémente donc que le getter aussi), c'est ce qui rend la propriété readonly.

    Je n'ai jamais fait usage des @private et @public en Objective-C pour ma part :
    • Les variables d'instance se doivent de toutes être privées. Si tu veux y accéder de l'extérieur, tu fais un getter + un setter si elle est writable, ou mieux tu fais une @property, et avec la syntaxe pointée tu pourras ainsi accéder à  tes variables comme si tu accédais à  des variables publiques alors qu'en fait ça appellera le setter/getter de façon transparente.
    • Les méthodes dans le .h sont toutes publiques
    • Pour déclarer des méthodes privées, tu fais une catégorie anonyme dans le .m. Ce qui est la façon standard de faire en Objective-C d'après la doc, et a en plus l'avantage de ne pas exposer ces méthodes privées dans le header (ce qui est plus logique que ce qu'on voit en C ou C++ où l'utilisateur voir les méthodes privées de la classe même alors qu'il ne peut pas les appeler puisqu'elles sont privées et ne devrait même pas savoir qu'elles existent)


    Donc au final tu fais tout avec des @property (c'est fait pour ça de toute façon) et tu gardes toutes tes v.i. privées. Donc la seule chose que tu as à  faire si tu ne veux pas que le KVC te foute la zone en risquant de modifier des variables auxquelles il ne devrait pas avoir accès, c'est de surcharger accessInstanceVariablesDirectly pour retourner NO.

    En effet dommage que ça soit pas la valeur par défaut. Je ne vais pas rentrer dans des considérations subtiles de conception, il y a certains cas où justement ça peut être pratique (pour mimiquer le paradigme de "friend class" en Obj-C par exemple), mais c'est vrai que ça reste assez rare, donc l'inverse (retourner NO par défaut, et surcharger quand on veut bypasser ça justement pour des cas subtils où c'est utile) aurait été préférable de la part d'Apple mais bon.
  • zoczoc Membre
    mai 2010 modifié #5
    dans 1273597204:
    (ce qui est plus logique que ce qu'on voit en C ou C++ où l'utilisateur voir les méthodes privées de la classe même alors qu'il ne peut pas les appeler puisqu'elles sont privées et ne devrait même pas savoir qu'elles existent)

    Et en l'occurrence, la notion de variables/méthodes protégées/privées n'apporte aucune protection en réalité en C++, car il suffit de changer dans un ".h" C++ les mots clés "private:" par "public:" pour contourner la protection, et ce même si on ne dispose pas du code source complet des classes (il n'y a rien dans les fichiers ".o" générés à  partir de source C++ qui permettent de forcer la visibilité des variables/méthodes). Tant que l'ordre des méthodes et des variables d'instance n'est pas changé, le .h restera compatible avec le code compilé.


    Donc oui, KVC n'est pas forcément très sûr, mais C++ ne l'est absolument pas plus. Seuls les langages n'ayant pas de notion de fichier de déclaration (C#, Java, entre autre) sont suffisamment solides pour assurer que les niveaux de visibilité seront respectés et incontournables (et encore...).







  • segaoufsegaouf Membre
    07:15 modifié #6
    Aligator, je suis globalement d'accord avec tout ce que tu dis, je voulais juste confirmation que par defaut les variables d'instance en objective-c sont private.

    Moi je viens de java, et c'est clair que j'ai rarement vu des variables en public avec un raisonnement qui se tenais derrière. La seule chose que je trouve dommage en java sur le public / protected / privée  c'est le fonctionnement du protected : j'aurais aimé qu'il permette uniquement à  une sous-classe d'avoir accès à  la méthode varibale, mais il donne accès à  tout le package ... Mais bon, qui suis-je pour critiquer :D.

    Zoc, je n'ai jamais pratiqué C++ et je ne le ferais jamais, ma question n'était pas du tout dans le sens "bouuhhh regarder objective-c c'est de la m***!" j'adore objective-c ! D'ailleurs, j'y pense tout les jours au boulot quand je code en java :p. J'aimerais bien un job en développement Mac mais c'est pas à  HSBC que je vais le trouver ^^.

    Merci pour vos réponses, c'est vraiment bizarre ce KVC ... Il va falloir faire attention pour mon super projet de-la-mort-qui-tue-qui-va-me-rendre-riche-pour-acheter-pleins-dipad.
  • mpergandmpergand Membre
    07:15 modifié #7
    dans 1273610275:

    Aligator, je suis globalement d'accord avec tout ce que tu dis, je voulais juste confirmation que par defaut les variables d'instance en objective-c sont private.


    protected pas private !
  • AliGatorAliGator Membre, Modérateur
    07:15 modifié #8
    Exact, mpergand ;) bonne précision :)

    Dans mes messages j'utilise le mot "privé" par opposition au mot "public", mais en effet j'aurais dû utiliser le terme "protected" pour le différencier du cas "@private";
    Les sous-classes ont bien accès aux v.i. des classes parentes en ObjC.

    D'ailleurs j'ai pas souvenir d'avoir vu un cas (même dans d'autres langages " vu qu'en Obj-C je n'ai pas ce pb de concepts pour ma part comme je le disais) où il y avait un intérêt à  utiliser des variables vraiment privées (et non juste protected) ?
  • segaoufsegaouf Membre
    07:15 modifié #9
    Bah en java, protected veux dire que tu donnes accès à  la variable aux classes filles, mais aussi aux classes du même package :/.

  • segaoufsegaouf Membre
    mai 2010 modifié #10
    Je double post parceque, pour je ne sais quels raisons, le site bug énormément sur mon mac avec safari.

    Access Levels  -JAVA
    ModifierClassPackageSubclassWorld
    publicYYYY
    protectedYYYN
    no modifierYYNN
    privateYNNN


    Ce que j'aurais aimé ,c'est que protected agisse comme cela :

    ModifierClassPackageSubclassWorld
    protectedYNYN    !
  • AliGatorAliGator Membre, Modérateur
    07:15 modifié #11
    C'est bizarre le Java qd même... j'aurais pensé que l'ordre de priorité des ensembles serait plutôt Class < Subclass < Package < World... bizarres leurs concepts
  • zoczoc Membre
    07:15 modifié #12
    le "protected" de java existe pour pallier au manque d'un équivalent au "friend" de C++ (qui est une notion complètement moche en POO mais qui est bien pratique parfois). Son "équivalent" (presque) en C# est "internal".


    Le "protected" de Java permet à  des classes d'un même package de "communiquer" entre elles tout en s'assurant que les variables/méthodes ne pourront pas être utilisées à  l'extérieur du package. Pratique quand on développe des packages complexes réutilisables (API par exemple).

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