Constantes

Bonjour a toutes et tous !


 


Je débute sur xcode et voudrais développer une appli pour mon iPhone 4, pour me simplifier la vie (je travaille dans la clim).


 


Elle est relativement basique qui va effectuer un calcul puis j'aimerai créer un pdf récapitulatif et l envoyé a mon boss.


 


J'ai donc créer un nouveau projet en Objective-C (via plusieurs tutos sur le net), avec les icones, launchimage ...


 


Je voudrais juste savoir comment créer des constantes ?


 


J'ai créer un fichier "Constantes.h" avec a l'intérieur  



#define "GP72" = 0
#define "GP90" = 0
#define "GP108" = 1.2
#define "GP126" = 3.3
#define "GP144" = 3.3

Ma façon est elle bonne ?


 


Et dans mon "ViewController.m"



#import "Constantes.h" // Load des constantes

Merci par avance !


Réponses

  • AliGatorAliGator Membre, Modérateur
    avril 2015 modifié #2
    Hello !

    Alors non, ce n'est pas une bonne façon.

    1) Mauvaise syntaxe

    Un "#define" ne fait que déclarer un terme à  rechercher/remplacer automatiquement par le compilateur lors de la compilation. Ici ça ne va pas remplacer toutes les occurrences de GP108 par 1.2, mais remplacer toutes les occurrences de "GP108" (note les guillemets, inclus). D'ailleurs je ne sais même pas si c'est une syntaxe valide, je ne pense pas (ton code ne doit sans doute pas compiler)

    Si tu voulais vraiment utiliser un #define, il faudrait plutôt écrire :
    #define GP108 1.2
    #define GP126 3.3
    Pour dire au compilateur "avant de compiler quoi que ce soit, fait un rechercher/remplacer dans mon code pour remplacer toute occurrence du token GP108 par 1.2, verbatim (au caractère près)".


    2) Mauvaise idée

    Mais de toute façon, utiliser "#define" est une mauvaise pratique pour déclarer des constantes. Certains l'utilisent (je ne sais pas si tu l'as vu dans un auto, peut-être), mais ce n'est pas une bonne idée, car :
    • Déjà , ce n'est pas initialement fait pour déclarer des constantes
    • Ensuite, on peut mettre un peu n'importe quoi dans un "#define", y compris juste une partie incomplète d'une expression, bref un peu ce qu'on veut, le compilateur ne vérifiera les choses qu'après avoir remplacé le token par le texte correspondant, du coup tu peux un peu injecter n'importe quoi dans ton code sans t'en rendre compte. Exemple:
      #define GP72 1.0 + 0.2 // Tu te dis que ça va faire 1.2
      CGFloat val = GP72 * 2 // Là  tu penses que ça va faire 1.2*2 = 2.4 ? Faux !
      // Ca va en fait faire val = 1.0 + 0.2 * 2 car ça remplace bêtement et simplement le texte, donc 1.4
    • Tu perds l'information de type. Et au passage tu perds la vérification du compilateur. Par exemple si pour toi tes valeurs GPxxx sont sensées être des float, le compilateur ne va pas te sortir d'erreur si tu passes par erreur GP72 à  une fonction qui prend un int au lieu d'un float, car GP72 sera remplacé par 0 qui est un int valide... et donc ton erreur passera inaperçue et tu ne verras pas le problème (et tu peux perdre des heures à  comprendre d'où vient un bug avec ce genre de choses surtout quand c'est marqué derrière une macro #define)
    • Puisque ce n'est que du texte recherché/remplacé par le précompilateur, si jamais il y a une erreur de compilation à  l'intérieur même de ta macro, cela devient très très vite illisible. Par exemple :
      #define X_CARRE x*x
      NSString* x = @Hello
      CGFloat val = X_CARRE // Une erreur ici, mais si tu ne connais pas la définition de X_CARRE, va comprendre le problème...
      Et encore évidemment là  j'ai pris des exemples simples ;)
    3) La bonne solution

    La bonne façon de déclarer une constante, c'est de la déclarer comme tu ferrais pour une variable, mais avec le mot clé "const" entre le type de la variable et le nom de la variable.

    [...La suite dans le prochain post...]
  • AliGatorAliGator Membre, Modérateur
    La bonne façon de déclarer une constante donc, c'est de la déclarer comme tu ferrais pour une variable, mais avec le mot clé "const" entre le type de la variable et le nom de la variable.

    Par exemple :

    float const GP72 = 0.0;
    float const GP90 = 0.0;
    float const GP108 = 1.2;
    float const GP126 = 3.3;
    float const GP144 = 3.3;
    Tu déclares ces lignes dans l'espace global (donc pas à  l'intérieur d'une fonction, ni d'une @interface ou @implementation, bref pas à  l'intérieur d'accolades mais directement au niveau racine).

    Note (très important) que c'est une très mauvaise idée de déclarer des variables globales (donc des variables au niveau racine d'un fichier, hors de tout contexte d'une classe ou d'une fonction), car n'importe qui pourrait modifier ces variables globales n'importe comment, sans protection, et cela peut poser des problèmes (multi-threading, etc, je ne rentrerai pas dans le détail ici). Par contre déclarer des constantes à  ce niveau est tout à  fait acceptable, et est même la bonne façon de faire.


    Il y a cependant quelques petites subtilités, car il y a 2 cas d'usage : des constantes locales à  ton fichier (dites "static"), et des constantes globales pour ton application (dites "extern")

    1) Constante locale à  ton fichier .m

    Si tu veux déclarer une constante mais qui ne doit être connue / accessible que dans le fichier .m courant (par exemple appelons-le "MaClasse.m"), et que ces constantes n'ont aucune raison d'être visibles/accessibles depuis les autres fichiers de ton application, alors la constante peut être déclarée dans le fichier .m directement, et le mot clé "static" va permettre de s'assurer que l'on confine cette variable uniquement au fichier courant :
    // Dans MonFichier.m
    static float const GP72 = 0.0;
    static float const GP90 = 0.0;
    static float const GP108 = 1.2;
    static float const GP126 = 3.3;
    static float const GP144 = 3.3;
    Dans ce cas, tu n'as pas à  déclarer ces constantes dans le fichier .h, normal puisque tu veux que ces constantes soient uniquement connues de MonFichier.m (et le .h est l'interface publique d'une classe, accessible de l'extérieur, donc si ces constantes doivent être confinées au fichier MonFichier.m elle n'ont pas à  apparaà®tre dans son interface publique)

    C'est un cas qui n'est pas si rare, par exemple pour les constantes permettant de déclarer des valeurs par défaut pour une classe, ou des constantes utilisées pour un calcul interne à  la classe mais qui n'ont aucune raison d'être exposées à  l'extérieur (en Programmation Orientée Objet, on préfère faire un maximum d'encapsulation et de n'exposer dans l'interface publique que le strict nécessaire)



    2) Constante globale à  toute l'application

    Si par contre tu veux déclarer une constante qui doit être visible dans toute l'application " et d'après ton exemple ça m'a l'air d'être ton cas " il ne faut pas la déclarer "static", bien au contraire, et il faut déclarer ces constantes dans le .h pour qu'elles soient visibles de tous. Le problème c'est que (1) dans un .h tu ne peux pas donner de valeur à  une constante, juste la déclarer " un .h ne contient que les déclaration, pas les valeurs, comme il ne contient que la liste des fonctions, pas leur code " donc il faudra que tu déclares la valeur dans un .m, et (2) si tu déclares la constante dans le .h sans le mot clé "extern", alors à  chaque fois que tu vas faire #import "MesConstantes.h" cela va re-déclarer ces constantes une nouvelle fois " il y aura autant de constantes nommées GP90 dans ton code que tu auras fait de #import "Constantes.h" dans ton application, et seule une de ces constantes aura pu avoir sa valeur associée dans le .m, les autres seront vues comme des constantes différentes, ayant certes le même nom mais c'est tout comme si elles en avaient un différent, tu auras créé N constantes nommée GP90...

    le mot clé "extern" permet d'éviter cela, en pratique il veut dire "j'informe sur l'honneur le compilateur qu'il existe quelque part une constante avec ce nom, mais c'est tout, ne prévois pas ici d'espace mémoire pour une nouvelle constante, je te dis juste qu'elle existe". Il ne va donc pas créer de constante à  chaque fois que tu vas faire le #import, il va juste être mis au courant qu'elle existe. Et c'est dans Constantes.m que tu vas à  la fois déclarer ET donner une valeur à  ta constante.

    Ce qui donne donc, in fine :

    // Dans ton MesConstantes.h
    extern float const GP72; // "extern" pour juste dire sur l'honneur qu'elle existe
    extern float const GP90; // et sera déclarée quelque-part ailleurs dans le code,
    extern float const GP108; // mais pas ici dans ce .h. C'est juste pour que le compilateur
    extern float const GP126; // ne sorte pas d'erreur s'il la rencontre, et qu'il sache à  quel
    extern float const GP144; // type de variable s'attendre
    // Dans ton fichier MesConstantes.m
    float const GP72 = 0.0; // Dans le .m on déclare et définit chaque constante
    float const GP90 = 0.0; // Mais au moins comme ça dans toute ton application
    float const GP108 = 1.2; // Chaque constante n'est déclarée et définie qu'une
    float const GP126 = 3.3; // seule et unique fois, dans ce fichier Constantes.m
    float const GP144 = 3.3; // et tu ne risques pas d'avoir plusieurs déclarations de la même constante
    // Dans ton fichier MaClasse.m qui a besoin de ces constantes
    // Récupérer les "extern float const ..." indiquant juste au compilateur que ces constantes existent
    // Mais, grâce au mot clé "extern", ne va pas risquer de les re-déclarer (et risquer de réserver à  nouveau
    // de l'espace mémoire pour elles alors qu'elles existent déjà  dans MesConstantes.m)
    #import "MesConstantes.h"

    @implementation MaClasse
    - (void)uneMethodeTest
    {
    [self autreMethodeAvecUnFloat:GP144];
    }
    - (void)autreMethodeAvecUnFloat:(float)unFloat
    {
    NSLog(@On a passé comme paramètre la valeur %f., unFloat);
    }
  • Merci AliGator ! 


     


    Un vrai cours !! j'ai eut beau avoir acheté "développer une application iphone pour les nuls" ce n'est pas aussi clair que ça !!


     


    Je me remet ça ce soir et vous tiens au courant.


     


    Merci encore !


  • Joanna CarterJoanna Carter Membre, Modérateur
    Je te conseillerais à  regarder les vidéos de Stanford University cours CS193p
  • LarmeLarme Membre
    avril 2015 modifié #6

    J's'rais d'avis de renommer le topic (Constantes ?) et de le mettre dans la section commune iOS/OSX.


  • AliGatorAliGator Membre, Modérateur
    Done.
  • Merci Joanna mais c'est vraiment hard !!


  • Bonjour, 


     


    En relisant ce sujet et la très claire explication d'Aligator je me suis posé une question... qu'en est-il de la question des constantes globales à  toute l'application pour Swift ? 


     


    Créer un fichier const.swift avec un listing de toutes les contantes sous forme de "let" est-il une solution viable et "propre" ?  


     


    Merci d'avance pour vos réponses.


  • Moi je fais comme ça et c'est viable. Maintenant à  savoir si c'est propre je me suis jamais posé la question...


  • AliGatorAliGator Membre, Modérateur
    Lisez mon article Enums as Constants sur mon blog (voir signature)
  • tabliertablier Membre
    octobre 2015 modifié #12

    Et qu'en est-il des trucs bien plus complexes.


    En raccourcis j'utilise en local dans un .m quelque chose, placé avant l'implémentation, comme:


     


    static struct htmCode{


      unichar k ;


      char *code ;


    } htmlcode[] = {


    {34,"quot"}, {38,"amp"}, {60,"lt"}, {62,"gt"} } ;


     


    Et je ne vois pas comment en faire des constantes. La table n'est que lue.


  • @tablier y a rien de complexe dans ton cas. Tu peux faire la même chose en Swift en déclarant une structure ensuite et un array de structure ou bien un enum avec valeur associées ( suivant la logique de ta structure de données).

  • tabliertablier Membre
    octobre 2015 modifié #14

    Je ne fais pas de swift ici. Ce que j'ai fais marche et pour moi, ce sont des variables. Ce que je ne vois pas, c'est comment en faire des constantes. 


  • Si par "constantes" tu veux que les champs de la struct ne puissent pas être modifiés, il faut déclarer la var struct const:



    typedef struct htmCode{
    char k ;
    char *code ;
    } HTM;

    static const HTM code[] = {
    {34,"quot"}, {38,"amp"}, {60,"lt"}, {62,"gt"} } ;


    code[0].code="??";


    produit une erreur:  assignment of read-only member 'code'


     


     


  • ça marche, merci !


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