Quelle est la différence entre une constante de chaîne et un littéral de chaîne?

J’apprends objective-C et Cocoa et je suis tombé sur cette déclaration:

Les frameworks Cocoa s’attendent à ce que les constantes de chaîne globales plutôt que les chaînes de caractères soient utilisées pour les clés de dictionnaire, les noms de notification et d’exception, et certains parameters de méthode prenant des chaînes.

Je n’ai travaillé que dans les langages de niveau supérieur, donc je n’ai jamais eu à tenir compte des détails des chaînes de caractères. Quelle est la différence entre une constante de chaîne et un littéral de chaîne?

En Objective-C, la syntaxe @"foo" est une instance littérale immuable de NSSsortingng . Il ne crée pas une chaîne constante à partir d’un littéral de chaîne, comme Mike le suppose.

Les compilateurs Objective-C exécutent généralement des chaînes littérales internes dans les unités de compilation – c’est-à-dire qu’ils fusionnent plusieurs utilisations de la même chaîne littérale – et l’éditeur de liens peut effectuer des opérations supplémentaires sur les unités de compilation directement liées en un seul. (Puisque Cocoa fait la distinction entre les chaînes mutables et immuables, et que les chaînes littérales sont toujours immuables, cela peut être simple et sûr.)

Les chaînes constantes, par contre, sont généralement déclarées et définies à l’aide de la syntaxe suivante:

 // MyExample.h - declaration, other code references this extern NSSsortingng * const MyExampleNotification; // MyExample.m - definition, comstackd for other code to reference NSSsortingng * const MyExampleNotification = @"MyExampleNotification"; 

Le but de l’exercice syntaxique ici est de rendre les utilisations de la chaîne efficaces en s’assurant qu’une seule instance de cette chaîne est utilisée, même sur plusieurs frameworks (bibliothèques partagées) dans le même espace d’adresse. (L’emplacement du mot clé const important; il garantit que le pointeur lui-même est garanti constant.)

Bien que la gravure de la mémoire ne soit pas aussi importante qu’auparavant avec les stations de travail 68030 à 25 MHz avec 8 Mo de mémoire vive, la comparaison des chaînes pour une égalité peut prendre du temps. Veiller à ce que la plupart du temps des chaînes égales soient également utiles.

Disons, par exemple, que vous souhaitez vous abonner aux notifications d’un object par son nom. Si vous utilisez des chaînes non constantes pour les noms, la publication de la notification par NSNotificationCenter pourrait se NSNotificationCenter par de nombreuses comparaisons de chaînes octet par octet pour déterminer qui est intéressé. Si la plupart de ces comparaisons sont court-circuitées parce que les chaînes comparées ont le même pointeur, cela peut être une grande victoire.

Quelques définitions

Un littéral est une valeur qui est immuable par définition. par exemple: 10
Une constante est une variable ou un pointeur en lecture seule. ex: const int age = 10;
Un littéral de chaîne est une expression comme @"" . Le compilateur le remplacera par une instance de NSSsortingng .
Une constante de chaîne est un pointeur en lecture seule vers NSSsortingng . ex: NSSsortingng *const name = @"John";

Quelques commentaires sur la dernière ligne:

  • C’est un pointeur constant, pas un object constant 1 . objc_sendMsg 2 ne se soucie pas si vous qualifiez l’object avec const . Si vous voulez un object immuable, vous devez coder cette immuabilité à l’intérieur de l’object 3 .
  • Toutes les expressions @"" sont en effet immuables. Ils sont remplacés 4 au moment de la compilation par des instances de NSConstantSsortingng , qui est une sous-classe spécialisée de NSSsortingng avec une disposition de mémoire fixe 5 . Cela explique également pourquoi NSSsortingng est le seul object pouvant être initialisé à la compilation 6 .

Une chaîne constante serait const NSSsortingng* name = @"John"; qui est équivalent à NSSsortingng const* name= @"John"; . Ici, l’intention de la syntaxe et du programmeur est erronée: const est ignoré et l’instance NSSsortingng ( NSConstantSsortingng ) était déjà immuable.

1 Le mot-clé const s’applique à tout ce qui se trouve immédiatement à sa gauche. S’il n’y a rien à sa gauche, cela s’applique à tout ce qui est immédiatement à sa droite.

2 Il s’agit de la fonction utilisée par le moteur d’exécution pour envoyer tous les messages en Objective-C, et par conséquent ce que vous pouvez utiliser pour modifier l’état d’un object.

3 Exemple: dans const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const n’empêche pas la dernière déclaration.

4 Le code LLVM qui réécrit l’expression est RewriteModernObjC::RewriteObjCSsortingngLiteral dans RewriteModernObjC.cpp.

5 Pour voir la définition de NSConstantSsortingng , cmd + cliquez dessus dans Xcode.

La création de constantes de temps de compilation pour d’autres classes serait facile, mais le compilateur devrait utiliser une sous-classe spécialisée. Cela romprait la compatibilité avec les anciennes versions d’Objective-C.


Retour à votre devis

Les frameworks Cocoa s’attendent à ce que les constantes de chaîne globales plutôt que les chaînes de caractères soient utilisées pour les clés de dictionnaire, les noms de notification et d’exception, et certains parameters de méthode prenant des chaînes. Vous devriez toujours préférer les constantes de chaîne aux littéraux de chaîne lorsque vous avez le choix. En utilisant des constantes de chaîne, vous obtenez l’aide du compilateur pour vérifier votre orthographe et éviter ainsi les erreurs d’exécution.

Il dit que les littéraux sont sujets aux erreurs. Mais cela ne dit pas qu’ils sont aussi plus lents. Comparer:

 // ssortingng literal [dic objectForKey:@"a"]; // ssortingng constant NSSsortingng *const a = @"a"; [dic objectForKey:a]; 

Dans le second cas, j’utilise des clés avec des pointeurs const, donc [a isEqualToSsortingng:b] , je peux le faire (a==b) . L’implémentation de isEqualToSsortingng: compare le hachage, puis exécute la fonction C strcmp , donc il est plus lent que de comparer directement les pointeurs. C’est pourquoi les chaînes constantes sont meilleures: elles sont plus rapides à comparer et moins sujettes aux erreurs.

Si vous voulez aussi que votre chaîne constante soit globale, faites comme ceci:

 // header extern NSSsortingng *const name; // implementation NSSsortingng *const name = @"john"; 

Utilisons C ++, car mon objective C est totalement inexistant.

Si vous cachez une chaîne dans une variable constante:

 const std::ssortingng myssortingng = "my ssortingng"; 

Maintenant, lorsque vous appelez des méthodes, vous utilisez my_ssortingng, vous utilisez une constante de chaîne:

 someMethod(myssortingng); 

Ou, vous pouvez appeler ces méthodes avec le littéral de chaîne directement:

 someMethod("my ssortingng"); 

La raison pour laquelle ils vous encouragent probablement à utiliser des constantes de chaîne est que l’objective C ne fait pas «d’internement»; En d’autres termes, lorsque vous utilisez le même littéral de chaîne à plusieurs endroits, il s’agit en réalité d’un pointeur différent qui pointe vers une copie distincte de la chaîne.

Pour les clés de dictionnaire, cela fait une énorme différence, car si je peux voir que les deux pointeurs pointent vers la même chose, c’est beaucoup moins cher que de devoir faire une comparaison de chaîne complète pour s’assurer que les chaînes ont la même valeur.

Edit: Mike, dans C # les chaînes sont immuables, et les chaînes littérales avec des valeurs identiques terminent toutes à la même valeur de chaîne. J’imagine que c’est vrai pour d’autres langages qui ont des chaînes immuables. Dans Ruby, qui a des chaînes mutables, elles offrent un nouveau type de données: symbols (“foo” vs.: foo, où la première est une chaîne modifiable, et la dernière est un identificateur immuable souvent utilisé pour les clés de hachage).