Constantes em Objective-C

Estou desenvolvendo um aplicativo Cocoa e estou usando NSString constantes como formas de armazenar nomes-chave para minhas preferências.

Eu entendo isso é uma boa idéia, porque permite a fácil troca de chaves, se necessário. Além disso, é o todo ‘separar seus dados de sua lógica’ noção.

De qualquer forma, existe uma boa maneira de fazer essas constantes definidas uma vez para o aplicativo inteiro? Tenho certeza de que há uma maneira fácil e inteligente, mas agora minhas aulas apenas redefinem as que elas usam.

Você deve criar um arquivo de header como

 // Constants.h FOUNDATION_EXPORT NSString *const MyFirstConstant; FOUNDATION_EXPORT NSString *const MySecondConstant; //etc. 

(você pode usar extern vez de FOUNDATION_EXPORT se seu código não for usado em ambientes mistos C / C ++ ou em outras plataformas)

Você pode include esse arquivo em cada arquivo que usa as constantes ou no header pré-compilado do projeto.

Você define essas constantes em um arquivo .m como

 // Constants.m NSString *const MyFirstConstant = @"FirstConstant"; NSString *const MySecondConstant = @"SecondConstant"; 

Constants.m deve ser adicionado ao destino da sua aplicação / framework para que ele seja vinculado ao produto final.

A vantagem de usar constantes de string em vez de constantes #define ‘d é que você pode testar a igualdade usando a comparação de pointers ( stringInstance == MyFirstConstant ) que é muito mais rápida que a comparação de strings ( [stringInstance isEqualToString:MyFirstConstant] ) e mais fácil de ler, IMO).

Caminho mais fácil:

 // Prefs.h #define PREFS_MY_CONSTANT @"prefs_my_constant" 

Melhor maneira:

 // Prefs.h extern NSString * const PREFS_MY_CONSTANT; // Prefs.m NSString * const PREFS_MY_CONSTANT = @"prefs_my_constant"; 

Um benefício do segundo é que alterar o valor de uma constante não causa uma reconstrução de todo o programa.

Há também uma coisa a mencionar. Se você precisar de uma constante não global, deverá usar a palavra-chave static .

Exemplo

 // In your *.m file static NSString * const kNSStringConst = @"const value"; 

Por causa da palavra-chave static , essa const não é visível fora do arquivo.


Correção menor por @QuinnTaylor : variables ​​estáticas são visíveis dentro de uma unidade de compilation . Normalmente, este é um único arquivo .m (como neste exemplo), mas pode ser mordido se você o declarar em um header que esteja incluído em outro lugar, já que você receberá erros de vinculação após a compilation

A resposta aceita (e correta) diz que “você pode include este arquivo [Constants.h] … no header pré-compilado do projeto.”

Como novato, eu tive dificuldade em fazer isso sem mais explicações – aqui está como: No seu arquivo YourAppNameHere-Prefix.pch (este é o nome padrão para o header pré-compilado no Xcode), importe seu Constants.h dentro do bloco #ifdef __OBJC__ .

 #ifdef __OBJC__ #import  #import  #import "Constants.h" #endif 

Observe também que os arquivos Constants.h e Constants.m devem conter absolutamente nada mais, exceto o que está descrito na resposta aceita. (Nenhuma interface ou implementação).

Eu geralmente estou usando o caminho postado por Barry Wark e Rahul Gupta.

Embora, eu não goste de repetir as mesmas palavras nos arquivos .h e .m. Note que, no exemplo a seguir, a linha é quase idêntica em ambos os arquivos:

 // file.h extern NSString* const MyConst; //file.m NSString* const MyConst = @"Lorem ipsum"; 

Portanto, o que eu gosto de fazer é usar algum maquinário de pré-processamento de C. Deixe-me explicar através do exemplo.

Eu tenho um arquivo de header que define a macro STR_CONST(name, value) :

 // StringConsts.h #ifdef SYNTHESIZE_CONSTS # define STR_CONST(name, value) NSString* const name = @ value #else # define STR_CONST(name, value) extern NSString* const name #endif 

No meu par .h / .m onde eu quero definir a constante eu faço o seguinte:

 // myfile.h #import  STR_CONST(MyConst, "Lorem Ipsum"); STR_CONST(MyOtherConst, "Hello world"); // myfile.m #define SYNTHESIZE_CONSTS #import "myfile.h" 

et voila, Eu tenho todas as informações sobre as constantes no arquivo.

Uma ligeira modificação da sugestão do @Krizz, para que funcione corretamente se o arquivo de header das constantes for incluído na PCH, o que é bastante normal. Como o original é importado para o PCH, ele não é recarregado no arquivo .m e, portanto, você não recebe símbolos e o vinculador não está satisfeito.

No entanto, a seguinte modificação permite que ele funcione. É um pouco confuso, mas funciona.

Você precisará de 3 arquivos, o arquivo .h que tenha as definições de constantes, o arquivo .h e o arquivo .m , eu usarei ConstantList.h , Constants.h Constants.m , respectivamente. o conteúdo de Constants.h é simplesmente:

 // Constants.h #define STR_CONST(name, value) extern NSString* const name #include "ConstantList.h" 

e o arquivo Constants.m parece com:

 // Constants.m #ifdef STR_CONST #undef STR_CONST #endif #define STR_CONST(name, value) NSString* const name = @ value #include "ConstantList.h" 

Finalmente, o arquivo ConstantList.h tem as declarações reais e isso é tudo:

 // ConstantList.h STR_CONST(kMyConstant, "Value"); … 

Algumas coisas a serem observadas:

  1. Eu tive que redefinir a macro no arquivo .m depois de #undef ele para a macro a ser usada.

  2. Eu também tive que usar #include vez de #import para que isso funcione corretamente e evite que o compilador veja os valores previamente pré-compilados.

  3. Isso exigirá uma recompilation de seu PCH (e provavelmente o projeto inteiro) sempre que algum valor for alterado, o que não é o caso se eles forem separados (e duplicados) normalmente.

Espero que seja útil para alguém.

Eu mesmo tenho um header dedicado a declarar NSStrings constantes usadas para preferências como:

 extern NSString * const PPRememberMusicList; extern NSString * const PPLoadMusicAtListLoad; extern NSString * const PPAfterPlayingMusic; extern NSString * const PPGotoStartupAfterPlaying; 

Em seguida, declarando-os no arquivo .m que o acompanha:

 NSString * const PPRememberMusicList = @"Remember Music List"; NSString * const PPLoadMusicAtListLoad = @"Load music when loading list"; NSString * const PPAfterPlayingMusic = @"After playing music"; NSString * const PPGotoStartupAfterPlaying = @"Go to startup pos. after playing"; 

Essa abordagem me serviu bem.

Edit: Note que isso funciona melhor se as seqüências de caracteres são usadas em vários arquivos. Se apenas um arquivo usa, você pode fazer #define kNSStringConstant @"Constant NSString" no arquivo .m que usa a string.

 // Prefs.h extern NSString * const RAHUL; // Prefs.m NSString * const RAHUL = @"rahul"; 

Como o Abizer disse, você poderia colocá-lo no arquivo PCH. Outra maneira que não é tão suja é fazer um arquivo de inclusão para todas as suas chaves e incluí-lo no arquivo em que você está usando as chaves ou incluí-lo no PCH. Com eles em seu próprio arquivo de inclusão, isso pelo menos lhe dá um lugar para procurar e definir todas essas constantes.

Se você quiser algo como constantes globais; uma maneira rápida e suja é colocar as declarações constantes no arquivo pch .

Eu uso uma class singleton, para que eu possa zombar da class e alterar as constantes, se necessário para testes. A class de constantes é assim:

 #import  @interface iCode_Framework : NSObject @property (readonly, nonatomic) unsigned int iBufCapacity; @property (readonly, nonatomic) unsigned int iPort; @property (readonly, nonatomic) NSString * urlStr; @end #import "iCode_Framework.h" static iCode_Framework * instance; @implementation iCode_Framework @dynamic iBufCapacity; @dynamic iPort; @dynamic urlStr; - (unsigned int)iBufCapacity { return 1024u; }; - (unsigned int)iPort { return 1978u; }; - (NSString *)urlStr { return @"localhost"; }; + (void)initialize { if (!instance) { instance = [[super allocWithZone:NULL] init]; } } + (id)allocWithZone:(NSZone * const)notUsed { return instance; } @end 

E é usado assim (observe o uso de um atalho para as constantes c – ele salva a digitação [[Constants alloc] init] toda vez):

 #import "iCode_FrameworkTests.h" #import "iCode_Framework.h" static iCode_Framework * c; // Shorthand @implementation iCode_FrameworkTests + (void)initialize { c = [[iCode_Framework alloc] init]; // Used like normal class; easy to mock! } - (void)testSingleton { STAssertNotNil(c, nil); STAssertEqualObjects(c, [iCode_Framework alloc], nil); STAssertEquals(c.iBufCapacity, 1024u, nil); } @end 

Tente usar um método de class:

 +(NSString*)theMainTitle { return @"Hello World"; } 

Eu uso isso às vezes.

Se você gosta de namespace constante, você pode aproveitar struct, Friday Q & A 2011-08-19: Constantes e Funções com Namespaces

 // in the header extern const struct MANotifyingArrayNotificationsStruct { NSString *didAddObject; NSString *didChangeObject; NSString *didRemoveObject; } MANotifyingArrayNotifications; // in the implementation const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = { .didAddObject = @"didAddObject", .didChangeObject = @"didChangeObject", .didRemoveObject = @"didRemoveObject" };