Qual é a diferença entre uma constante de string e uma string literal?

Estou aprendendo objective-C e cacau e me deparei com esta declaração:

As estruturas Cocoa esperam que as constantes de cadeia global, em vez de literais de cadeia, sejam usadas para chaves de dictionary, nomes de notificação e exceção e alguns parâmetros de método que usam sequências de caracteres.

Eu só trabalhei em linguagens de alto nível, então nunca tive que considerar os detalhes das strings tanto assim. Qual é a diferença entre uma constante de string e string literal?

Em Objective-C, a syntax @"foo" é uma instância literal e imutável de NSString . Não faz uma string constante de uma string literal como Mike supõe.

Os compiladores Objective-C normalmente executam sequências literais internas em unidades de compilation – ou seja, combinam vários usos da mesma cadeia literal – e é possível que o vinculador faça internações adicionais nas unidades de compilation diretamente vinculadas a um único binário. (Como o Cocoa distingue entre cadeias mutáveis ​​e imutáveis, e cadeias literais são sempre imutáveis, isso pode ser direto e seguro.)

Strings constantes, por outro lado, são tipicamente declaradas e definidas usando syntax como esta:

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

O ponto do exercício sintático aqui é que você pode tornar eficientes os usos da cadeia de caracteres garantindo que haja apenas uma instância dessa sequência em uso, mesmo em várias estruturas (bibliotecas compartilhadas) no mesmo espaço de endereço. (O posicionamento da palavra-chave const importante; garante que o próprio ponteiro tenha a garantia de ser constante.)

Embora a gravação de memory não seja tão grande quanto pode ter sido nos dias de estações de trabalho de 68030 com 8 MB de RAM, comparar strings para igualdade pode levar tempo. Garantir que na maioria das vezes as strings iguais sejam também ajudas de pointers iguais.

Por exemplo, digamos que você deseja se inscrever em notifications de um object por nome. Se você usar strings não constantes para os nomes, o NSNotificationCenter publica a notificação pode acabar fazendo muitas comparações de string byte-by-byte ao determinar quem está interessado nela. Se a maioria dessas comparações estiver em curto-circuito porque as strings comparadas têm o mesmo ponteiro, isso pode ser uma grande vitória.

Algumas definições

Um literal é um valor, que é imutável por definição. por exemplo: 10
Uma constante é uma variável ou ponteiro somente leitura. por exemplo: const int age = 10;
Uma string literal é uma expressão como @"" . O compilador irá replace isso por uma instância de NSString .
Uma constante de seqüência de caracteres é um ponteiro somente leitura para NSString . por exemplo: NSString *const name = @"John";

Alguns comentários na última linha:

  • Esse é um ponteiro constante, não um object constante 1 . objc_sendMsg 2 não se importa se você qualifica o object com const . Se você quer um object imutável, você tem que codificar essa imutabilidade dentro do object 3 .
  • Todas as expressões @"" são realmente imutáveis. Eles são substituídos em tempo de compilation em 4 com instâncias de NSConstantString , que é uma subclass especializada de NSString com um layout de memory fixa 5 . Isso também explica porque o NSString é o único object que pode ser inicializado em tempo de compilation 6 .

Uma string constante seria const NSString* name = @"John"; que é equivalente a NSString const* name= @"John"; . Aqui, tanto a syntax quanto a intenção do programador estão erradas: const é ignorado, e a instância NSString ( NSConstantString ) já era imutável.

1 A palavra-chave const aplica-se a qualquer coisa que esteja imediatamente à sua esquerda. Se não houver nada à sua esquerda, isso se aplica ao que está imediatamente à sua direita.

2 Essa é a function que o runtime usa para enviar todas as mensagens em Objective-C e, portanto, o que você pode usar para alterar o estado de um object.

3 Exemplo: em const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const não impede a última declaração.

4 O código LLVM que reescreve a expressão é RewriteModernObjC::RewriteObjCStringLiteral em RewriteModernObjC.cpp.

5 Para ver a definição NSConstantString , cmd + clique no Xcode.

6 Criar constantes de tempo de compilation para outras classs seria fácil, mas exigiria que o compilador usasse uma subclass especializada. Isso quebraria a compatibilidade com versões antigas do Objective-C.


De volta ao seu orçamento

As estruturas Cocoa esperam que as constantes de cadeia global, em vez de literais de cadeia, sejam usadas para chaves de dictionary, nomes de notificação e exceção e alguns parâmetros de método que usam sequências de caracteres. Você deve sempre preferir constantes de string sobre literais de string quando tiver uma opção. Usando constantes de string, você solicita a ajuda do compilador para verificar sua ortografia e, assim, evitar erros de tempo de execução.

Diz que os literais são propensos a erros. Mas isso não diz que eles também são mais lentos. Comparar:

 // string literal [dic objectForKey:@"a"]; // string constant NSString *const a = @"a"; [dic objectForKey:a]; 

No segundo caso eu estou usando chaves com pointers const, então ao invés de [a isEqualToString:b] , eu posso fazer (a==b) . A implementação de isEqualToString: compara o hash e, em seguida, executa a function C strcmp , portanto, é mais lento do que comparar os pointers diretamente. É por isso que as strings constantes são melhores: elas são mais rápidas de comparar e menos propensas a erros.

Se você também quiser que sua string constante seja global, faça assim:

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

Vamos usar o C ++, já que meu objective C é totalmente inexistente.

Se você esconder uma string em uma variável constante:

 const std::string mystring = "my string"; 

Agora quando você chama methods, você usa my_string, você está usando uma constante de string:

 someMethod(mystring); 

Ou você pode chamar esses methods diretamente com a string literal:

 someMethod("my string"); 

A razão, presumivelmente, de que eles encorajam você a usar constantes de string é porque o Objective C não faz “interning”; ou seja, quando você usa o mesmo literal de string em vários lugares, na verdade é um ponteiro diferente apontando para uma cópia separada da string.

Para as chaves de dictionary, isso faz uma grande diferença, porque se eu puder ver os dois pointers apontando para a mesma coisa, isso é muito mais barato do que ter que fazer uma comparação de string inteira para garantir que as strings tenham o mesmo valor.

Edit: Mike, em seqüências de caracteres C # são imutáveis ​​e seqüências de caracteres literais com valores idênticos todos final apontando para o mesmo valor de seqüência de caracteres. Eu imagino que isso seja verdade também para outras linguagens que possuem strings imutáveis. Em Ruby, que possui strings mutáveis, eles oferecem um novo tipo de dados: symbols (“foo” vs.: foo, onde o primeiro é uma string mutável, e o último é um identificador imutável frequentemente usado para chaves Hash).