Constante associée à  une classe, via une catégorie

colas_colas_ Membre
octobre 2015 modifié dans API AppKit #1

Bonsoir,


 


suivant les conseils donnés ici par certains, j'essaie de limiter l'usage des "variables" de classe.


 


Néanmoins, pour des raisons d'optimisation prématurée :P, je voulais avoir votre avis sur cet extrait de code :



@interface NSDate(MyCategory)
- (NSString *)frenchPrint;
@end

@implementation NSDate(MyCategory)

static NSLocale * frenchLocale ;

- (NSString *)frenchPrint
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
frenchLocale = [[NSLocale alloc] initWithLocaleIdentifier:@fr_FR];
});

NSDateFormatter *formatterForTheDay = [[NSDateFormatter alloc] init];
formatterForTheDay.locale = frenchLocale;

//etc.
}


@end

Est-ce ok ?


 


 


En particulier, si dans une autre catégorie au-dessus de NSDate, j'ai aussi



static NSLocale * frenchLocale ;

est-ce ok ?


 


 


Merci 


 


 


PS : j'ai déjà  une idée pour améliorer ce code



static NSLocale * _frenchLocale ;
- (NSLocale *)frenchLocale_MyCategory
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_frenchLocale = [[NSLocale alloc] initWithLocaleIdentifier:@fr_FR];
});

return _frenchLocale;
}

Réponses

  • Hello,


     


    Je crois que c'est plutôt le formater qu'il faut éviter de créer plusieurs fois et non pas NSLocale.



    static  NSDateFormatter *  formatterForTheDay.

    - (NSString *)frenchPrint
    {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    formatterForTheDay = [[NSDateFormatter alloc] init];
    NSLocale *frenchLocale = [[NSLocale alloc] initWithLocaleIdentifier:@fr_FR];
    formatterForTheDay.locale = frenchLocale;
    });

    }

    La deuxième question je ne l'ai pas trop saisie. static pour une variable globale veut dire que cette ladite variable est accessible par toute méthode/fonction dans le même fichier source que cette variable.


  • AliGatorAliGator Membre, Modérateur
    En mettant "static" au niveau du fichier (file-level) comme ça effectivement la variable sera uniquement visible et accessible pas le code qui est dans le même fichier.


    ça restera quand même dangereux car une variable "semi-globale" on va dire, puisque n'importe quel code dans le fichier pourra y accéder sans protection en particulier sans thread-safety.

    Et surtout cette variable globale n'est pas liée à  une instance mais partagée par toutes les instances (ça c'est le but du static et c'est sans doute voulu dans ton cas ceci dit)


    Il serait bien mieux de mettre cette variable "static" non pas au niveau racine du fichier mais plutôt à  l'intérieur de la méthode frenchPrint. Exactement sur le même modèle que tu as fait pour le "dispatch_once_t onceToken" en fait.

    Comme ça la variable n'est accessible que depuis l'intérieur de cette méthode, et invisible de l'extérieur de cette méthode, que ce soit dans les autres méthodes de cette catégorie où d'une autre catégorie ou de la classe en question, donc pas de risque de conflit.


    Après je confirme ce que dit samir ce qui prend le plus de temps à  initialiser et qu'il faut donc mettre en cache pour l'initialiser qu'une fois pour toutes c'est plutôt le NSDateFormatter que la NSLocale qui elle ne coûte pas grand chose à  initialiser.


    Donc en fait prendre le code de samir MAIS mettre le "static NSDateFormatter" à  l'intérieur de ta méthode de la même manière que tu as fait pour le onceToken.
  • AliGatorAliGator Membre, Modérateur
    Sinon un peu hors sujet de la question postée dans le titre d'origine, mais concernant les NSDateFormatter et le fait de les mettre en cache pour éviter de les recréer à  chaque fois (car ils font partie avec NSRegularExpression des classes qui prennent un temps significatif pour d'instance et par rapport à  la normale et la moyenne des autres classes), voir aussi ce lien : https://developer.apple.com/library/mac/qa/qa1480/_index.html
  • Merci Samir.


     


    Quand on parle de fichier source, il s'agit vraiment du même fichier ? Je crois que je suis le prototype de futur adepte de Swift, car j'ai appris l'objective-C sans connaà®tre le C et je trimballe des faiblesses !


     


    Toutes mes catégories sont dans des fichiers séparés et je voudrais utiliser ce même procédé dans plusieurs catégories. Donc, je voulais savoir si avec des static avec le même nom était ok.


     


    D'après ce que tu dis, c'est ok, n'est-ce pas ?




  •  pas de risque de conflit.

     




     


    De quel conflit parles-tu ?


     


    En effet, je n'écris cette variable que dans le dispatch_once.


    J'aurai bien ajouté un `const` mais le compilo ne veut pas.




  • Il serait bien mieux de mettre cette variable "static" non pas au niveau racine du fichier mais plutôt à  l'intérieur de la méthode frenchPrint. Exactement sur le même modèle que tu as fait pour le "dispatch_once_t onceToken" en fait.

    Comme ça la variable n'est accessible que depuis l'intérieur de cette méthode, et invisible de l'extérieur de cette méthode, que ce soit dans les autres méthodes de cette catégorie où d'une autre catégorie ou de la classe en question, donc pas de risque de conflit.

     




     


     


    En fait, il se peut que j'utilise le NSLocale (ou le NSCalendar) depuis plusieurs méthodes.


     


    @Ali

    J'ai l'impression que tu te contredis car d'un côté tu dis que la variable ne sera accessible que depuis le fichier et de l'autre tu dis qu'il faut la protéger pour qu'elle ne soit pas vue par la classe ou par d'autres catégories. Erreur de ta part ?

  • Je vais faire la même chose pour NSFormatter.


     


    En gros, j'affiche des dates dans une table view...


  • AliGatorAliGator Membre, Modérateur

    Toutes mes catégories sont dans des fichiers séparés et je voudrais utiliser ce même procédé dans plusieurs catégories. Donc, je voulais savoir si avec des static avec le même nom était ok.
     
    D'après ce que tu dis, c'est ok, n'est-ce pas ?

    Oula... heu oui et non en fait. Si tu mets une "static NSLocale* frenchLocale"  dans chacun de tes fichiers (par exemple dans le fichier .m où tu implémentes ta classe et un aussi dans le fichier .m où tu implémentes ta catégorie), ce seront du coup 2 variables totalement différentes. Comme si elles avaient des noms différents. Car chacune est isolée au fichier dans lequel elle est déclarée. C'est ce que veux dire "static" dans ce contexte.

    Alors :
    - peut-être que c'est ce que tu veux, d'avoir des variables qui ont le même nom mais qui en fait sont 2 variables totalement différentes entre les deux fichiers, mais franchement je te le déconseille, car c'est des coups à  confondre tout et à  plus savoir de quelle variable on parle (sympa pour débuguer aussi...), donc dans ce cas préfère utiliser explicitement des noms différents, ça t'évitera des incompréhensions plus tard quand tu te reliras ou devra débuguer
    - ou alors si tu veux que ça représente en fait la même variable (le même emplacement mémoire, etc, donc si tu changes la valeur depuis un fichier ce changement soit aussi visible dans l'autre fichier), alors ce n'est pas une variable "static" qu'il te faut. A la limite c'est plutôt avec "extern" que tu vas devoir jouer (je vais expliquer ça dans un post séparé)
     

    En fait, il se peut que j'utilise le NSLocale (ou le NSCalendar) depuis plusieurs méthodes.

    Dans ce cas, fais-en une méthode dans ta classe. Surtout si c'est une constante lady-initialisée lors de son premier accès, ça conviendrait bien mieux. Et tu peux faire pareil pour ton NSDateFormatter. Du style :


    + (NSLocale *)frenchLocale
    {
    static NSLocale *frLocale;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@fr_FR];
    });
    return frLocale;
    }
    + (NSLocale *)frenchFormatterForDay
    {
    static NSDateFormatter *formatterForTheDay;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    formatterForTheDay = [[NSDateFormatter alloc] init];
    formatterForTheDay.locale = [self frenchLocale];
    });
    return formatterForTheDay;
    }
    Comme toute variable déclarée à  l'intérieur d'un block entre accolades, tes variables frLocale et formatterForTheDay ne seront visible/accessible que dans ce block entre accolades, donc ici ne seront visible qu'à  l'intérieur du code de la fonction en question.

    Par contre elles utilisent le mot clé "static" qui, utilisé dans ce contexte (= à  l'intérieur d'une fonction, et pas au niveau du fichier) signifie que la variable gardera sa valeur entre chaque appel à  la fonction, et sera en fait globale, non pas au sens "accessible globalement" mais au sens "partagé par toutes les instances de ta classe, car ne sera pas lié à  ta classe et à  ton instance comme peut l'être une @property, mais va continuer à  garder sa valeur pendant toute la vie de l'application donc y compris entre plusieurs appels à  cette méthode.

    @Ali
    J'ai l'impression que tu te contredis car d'un côté tu dis que la variable ne sera accessible que depuis le fichier et de l'autre tu dis qu'il faut la protéger pour qu'elle ne soit pas vue par la classe ou par d'autres catégories. Erreur de ta part ?

    Non non, je ne me contredis pas.

    Si tu utilises une "static" au niveau du fichier, elle sera accessible que depuis le fichier. Ca ne l'empêche pas d'être vue par d'autres classes ou catégories... qui seraient déclarées dans le même fichier. J'ai bien dit:

    cette variable "semi-globale" on va dire, puisque n'importe quel code dans le fichier pourra y accéder

  • AliGatorAliGator Membre, Modérateur
    octobre 2015 modifié #10

    A la limite c'est plutôt avec "extern" que tu vas devoir jouer (je vais expliquer ça dans un post séparé)

    Et en fait j'avais déjà  précédemment expliqué tout ça dans un long post détaillé que tu retrouveras ici.


  •  


    Non non, je ne me contredis pas.

     


     


     


    Je faisais référence à 


     


     





    Comme ça la variable n'est accessible que depuis l'intérieur de cette méthode, et invisible de l'extérieur de cette méthode, que ce soit dans les autres méthodes de cette catégorie où d'une autre catégorie ou de la classe en question, donc pas de risque de conflit.

     




     


     


     


    Sinon, il faut faire attention avec tes méthode de classe encapsulant les static car si je crée la même méthode de classe dans une autre catégorie, je risque d'avoir des problèmes.


     


    D'où plutôt le code :



    + (NSLocale *)frenchLocale_<#nameCategory#>
    {
    static NSLocale *frLocale;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@fr_FR];
    });
    return frLocale;
    }
    + (
  • Sinon, autre lien intéressant sur la NSCalendar


     


    http://mikeabdullah.net/NSCalendar_currentCalendar.html

  • AliGatorAliGator Membre, Modérateur
    Petit addendum à  mes posts précédents et à  mon post explicatif sur l'usage de "static" pour déclarer des constantes locales aux fichiers.

    Note qu'il y a une différence de signification entre le cas où tu utilises "static" à  la racine d'un fichier et quand tu l'utilises pour déclarer une variable à  l'intérieur d'un block entre accolades (notamment à  l'intérieur d'une fonction donc)

    - "static" utilisé pour une déclaration à  la racine d'un fichier veux dire "cette variable n'est visible / accessible que depuis le fichier. Par contre n'importe qui dans le fichier pourra y avoir accès, dans ce sens elle est "globale" car "partagée par tous les trucs déclarés dans le même fichier (y compris par toutes les classes ou catégories que tu mettrais dans ce fichier si jamais tu faisais le choix bizarre de mettre plusieurs classes ou catégories dans un même gros fichier alors que la tendance est plutôt, comme tu le fais, de les mettre dans des fichiers séparés).

    Du coup dans ce contexte le mot clé "static" sert plutôt pour dire "je restreint l'accès et la visibilité de cette variable uniquement à  ce fichier, et pas aux autres fichiers".

    - "static" utilisé à  l'intérieur d'un bloc entre accolades, genre dans une fonction, veut plutôt dire "cette variable aura un état global partagé par tout les appels à  cette fonction. Elle gardera sa valeur même entre 2 appels à  cette fonction.
    Le fait que cette variable ne soit accessible que par le code à  l'intérieur de la même fonction n'est du coup pas trop lié au fait qu'elle soit déclarée "static", puisque ça c'est déjà  le cas de n'importe quelle variable déclarée dans le corps d'une fonction, elle n'est accessible/visible que par le code dans le même bloc d'accolades.
    Du coup dans ce contexte le "static" sert plutôt pour dire "en plus d'être comme les autres variables déclarées dans un bloc entre accolades qui ne sont pas visibles à  l'extérieur de ces accolades, je marque cette variable colle "static" pour dire qu'elle doit continuer à  vivre même quand on sort de la fonction, et ainsi quand on re-rentrera dans la fonction on retrouvera la valeur qu'elle avait la dernière fois.


    Du coup, ce mot clé "static" est souvent difficile à  appréhender car il n'a pas la même signification/interprétation en pratique selon s'il est utilisé pour une déclaration "top-level" (à  la racine du fichier) ou une déclaration à  l'intérieur d'une fonction... (et du coup y'a de quoi se mélanger les pinceaux). Même mot clé dans les 2 cas, mais deux cas d'usage pourtant bien différents...
  • AliGatorAliGator Membre, Modérateur

    Je faisais référence à 

    Et ? Je ne comprends pas trop ce qui te pose problème dans cette phrase citée en fait ? 
     
     

    Sinon, il faut faire attention avec tes méthode de classe encapsulant les static car si je crée la même méthode de classe dans une autre catégorie, je risque d'avoir des problèmes.

    Certes, mais je ne comprend pas trop le rapport avec l'usage des static ou pas ?!

    Si tu implémentes 2 fois la même méthode dans 2 catégories différentes d'une même classe, ou même que tu implémentes dans une catégorie une méthode qui est déjà  implémentée dans une classe, de toute façon tu vas avoir des problèmes, car tu auras 2 implémentations différentes pour une même méthode.
    • Que cette méthode soit une méthode de classe ou une méthode d'instance n'y change rien
    • Que ces implémentations de méthode utilisent des static ou pas n'y change rien
    • Et normalement si tu as 2 implémentations pour la même méthode (méthode avec le même nom pour la même classe " ou catégorie sur cette classe), tu vas avoir un warning au Runtime normalement qui te dit en gros que "telle Méthode est implémentée à  la fois dans telle classe et telle catégorie de cette classe, et que seule l'une des deux va être utilisée, quant à  savoir laquelle c'est un jeu de dés". En bref, ça serait une très mauvaise chose que de faire ça, on est d'accord. Mais tout ça n'a rien à  voir avec "static ou pas" ou avec "parce que c'est une méthode de classe", c'est une toute autre discussion qui n'a rien à  voir avec les constantes, mais plutôt avec les bonnes pratiques de préférer préfixer ou suffixer les noms des méthodes qu'on écrit dans une catégorie pour qu'il n'y ait pas de conflit, donc c'est un tout autre sujet qui n'a aucun lien avec "static"
  • Je voudrais juste rajouter une chose : A chaque fois qu'on veut créer une seule instance (comme l'exemple de @colas) on pense directement au dispatch_one alors que dans la majorité des cas on en a pas besoin et une simple variable static suffit.


     


    Utiliser dispatch_one si concurrence éventuelle de plusieurs threads


    sinon une simple variable static. ( un test == nil)

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