web-dev-qa-db-fra.com

Quand utiliser une chaîne statique par rapport à #define

Je suis un peu confus quant au meilleur moment pour l'utiliser:

static NSString *AppQuitGracefullyKey = @"AppQuitGracefully";

au lieu de

#define AppQuitGracefullyKey    @"AppQuitGracefully"

J'ai vu des questions comme celle-ci pour C ou C++, et je pense que ce qui est différent ici, c'est que c'est spécifiquement pour Objective C, en utilisant un objet, et sur un appareil comme l'iPhone, il peut y avoir des problèmes de pile, d'espace de code ou de mémoire qui Je ne saisis pas encore.

Une utilisation serait:

appQuitGracefully =  [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey];

Ou c'est juste une question de style?

Merci.

45
mahboudz

Si vous utilisez un statique, le compilateur incorporera exactement une copie de la chaîne dans votre binaire et passera simplement des pointeurs vers cette chaîne, ce qui donnera des binaires plus compacts. Si vous utilisez un #define, il y aura une copie séparée de la chaîne stockée dans la source à chaque utilisation. La coalescence constante des chaînes gérera de nombreux doublons, mais vous faites travailler l'éditeur de liens plus dur sans raison.

60
cdespinosa

Voir "const statique" vs "#define" vs "enum" . Le principal avantage de static est la sécurité des types.

À part cela, le #define l'approche introduit une flexibilité de concaténation de chaînes en ligne qui ne peut pas être effectuée avec des variables statiques, par exemple.

#define ROOT_PATH @"/System/Library/Frameworks"
[[NSBundle bundleWithPath:ROOT_PATH@"/UIKit.framework"] load];

mais ce n'est probablement pas un bon style :).

14
kennytm

En fait, je ne recommanderais ni l'un ni l'autre, vous devriez utiliser extern à la place. Objective-c définit déjà FOUNDATION_EXPORT Qui est plus portable que extern, donc une instance globale de NSString ressemblerait à ceci:

.h

FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey;

.m

NSString * const AppQuitGracefullyKey = @"AppQuitGracefully";

Je les mets généralement dans des fichiers de déclaration (tels que MyProjectDecl.h) Et les importe quand j'en ai besoin.

Il existe quelques différences entre ces approches:

  • # define a plusieurs inconvénients, comme le fait de ne pas être sûr du type. Il est vrai qu'il existe des solutions de contournement pour cela (comme #define ((int)1)) mais quel est le point? De plus, cette approche présente des inconvénients de débogage. Les compilateurs préfèrent les constantes. Voir cette discussion.
  • les globales statiques sont visibles dans le fichier, ils sont déclarés.
  • extern rend la variable visible à tous les fichiers. Cela contraste avec l'électricité statique.

La visibilité statique et externe diffèrent. Il est également à noter qu'aucune de ces approches ne duplique la chaîne (pas même #define) Car le compilateur utilise String Interning pour empêcher cela. Dans ce post NSHipster ils montrent la preuve:

NSString *a = @"Hello";
NSString *b = @"Hello";
BOOL wtf = (a == b); // YES

L'opérateur == Ne renvoie YES que si les deux variables pointent vers la même instance. Et comme vous pouvez le voir, c'est le cas.

La conclusion est: utilisez FOUNDATION_EXPORT Pour les constantes globales. Il est convivial pour le débogage et sera visible dans tout votre projet.

4
André Fratelli

Après avoir fait quelques recherches ( this question/réponse, entre autres), je pense qu'il est important de dire qu'à tout moment lorsque vous utilisez un littéral de chaîne @"AppQuitGracefully" une chaîne constante est créée, et peu importe combien de fois vous l'utilisez, elle pointera vers le même objet .

Je pense donc (et je m'excuse si je me trompe) que cette phrase dans la réponse ci-dessus est fausse: If you use a #define, there will be a separate copy of the string stored in the source on each use.

4
Aleksa

TILISATION #define:

vous ne pouvez pas déboguer la valeur de l'identifiant

travailler avec #define et d'autres macros est un travail de pré-processeur, lorsque vous appuyez d'abord sur Build/Run, il pré-traitera le code source, il fonctionnera avec toutes les macros (en commençant par le symbole #),

Supposons que vous ayez créé,

#define LanguageTypeEnglish @"en"

et utilisé cela à 2 endroits dans votre code.

NSString *language = LanguageTypeEnglish;
NSString *languageCode = LanguageTypeEnglish;

il remplacera "LanguageTypeEnglish" par @"en", partout. Donc 2 copies de @"en" sera généré. c'est à dire

NSString *language = @"en";
NSString *languageCode = @"en";

Rappelez-vous, jusqu'à ce processus, le compilateur n'est pas dans l'image.

Après le prétraitement de toutes les macros, complier apparaît dans l'image, et il obtiendra le code d'entrée comme ceci,

NSString *language = @"en";
NSString *languageCode = @"en";

et compilez-le.

TILISATION statique:

il respecte la portée et est de type sûr. vous pouvez déboguer la valeur de l'identifiant

Pendant le processus de compilation si le compilateur est trouvé,

static NSString *LanguageTypeRussian = @"ru";

alors il vérifiera si la variable avec le même nom stocké précédemment, si oui, il ne passera que le pointeur de cette variable, sinon, il créera cette variable et passera son pointeur, la prochaine fois il passera seulement le pointeur de le même.

Ainsi, en utilisant statique, une seule copie de variable est générée dans la portée.

3
Hitendra Solanki

J'utilise static lorsque j'ai besoin d'exporter des symboles NSString à partir d'une bibliothèque ou d'un framework. J'utilise #define quand j'ai besoin d'une chaîne à plusieurs endroits que je peux changer facilement. Quoi qu'il en soit, le compilateur et l'éditeur de liens se chargeront des optimisations.

3
Laurent Etiemble