Constantes en Objective-C

Estoy desarrollando una aplicación Cocoa , y estoy usando NSString constantes como formas de almacenar nombres clave para mis preferencias.

Entiendo que esta es una buena idea, ya que permite un fácil cambio de claves si es necesario. Además, es toda la noción de ‘separar sus datos de su lógica’.

De todos modos, ¿hay una buena manera de hacer que estas constantes se definan una vez para toda la aplicación? Estoy seguro de que hay una manera fácil e inteligente, pero en este momento mis clases simplemente redefinen las que usan.

Deberías crear un archivo de cabecera como

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

(Puede usar extern lugar de FOUNDATION_EXPORT si su código no se usará en entornos combinados de C / C ++ o en otras plataformas)

Puede incluir este archivo en cada archivo que use las constantes o en el encabezado precomstackdo para el proyecto.

Usted define estas constantes en un archivo .m como

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

Constants.m debe agregarse al objective de su aplicación / marco para que esté vinculado al producto final.

La ventaja de usar constantes de cadena en lugar de constantes #define ‘d es que puede probar la igualdad usando la comparación de punteros ( stringInstance == MyFirstConstant ) que es mucho más rápido que la comparación de cadenas ( [stringInstance isEqualToString:MyFirstConstant] ) (y más fácil de leer, IMO).

La manera más fácil:

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

Mejor manera:

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

Uno de los beneficios de la segunda es que cambiar el valor de una constante no causa la reconstrucción de todo el progtwig.

También hay una cosa para mencionar. Si necesita una constante no global, debe usar una palabra clave static .

Ejemplo

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

Debido a la palabra clave static , esta const no es visible fuera del archivo.


Corrección menor por @QuinnTaylor : las variables estáticas son visibles dentro de una unidad de comstackción . Por lo general, este es un archivo .m único (como en este ejemplo), pero puede morder si lo declaras en un encabezado que se incluye en otra parte, ya que obtendrás errores del enlazador luego de la comstackción.

La respuesta aceptada (y correcta) dice que “puede incluir este archivo [Constants.h] … en el encabezado precomstackdo para el proyecto”.

Como novato, tuve dificultades para hacer esto sin más explicaciones: así es cómo: en su archivo YourAppNameHere-Prefix.pch (este es el nombre predeterminado para el encabezado precomstackdo en Xcode), importe sus Constantes.h dentro del bloque #ifdef __OBJC__ .

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

También tenga en cuenta que los archivos Constants.h y Constants.m no deben contener absolutamente nada más que lo que se describe en la respuesta aceptada. (Sin interfaz o implementación).

En general estoy usando el camino publicado por Barry Wark y Rahul Gupta.

Aunque, no me gusta repetir las mismas palabras en los archivos .h y .m. Tenga en cuenta que en el siguiente ejemplo, la línea es casi idéntica en ambos archivos:

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

Por lo tanto, lo que me gusta hacer es usar maquinaria de preprocesador C. Déjame explicarte a través del ejemplo.

Tengo un archivo de cabecera que define la 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 

El en mi par .h / .m donde quiero definir la constante, hago lo siguiente:

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

et voila, tengo toda la información sobre las constantes en el archivo .h solamente.

Una ligera modificación de la sugerencia de @Krizz, para que funcione correctamente si el archivo de encabezado de constantes debe incluirse en el PCH, lo cual es bastante normal. Como el original se importa a PCH, no lo volverá a cargar en el archivo .m y, por lo tanto, no obtendrá ningún símbolo y el vinculador no estará contento.

Sin embargo, la siguiente modificación le permite funcionar. Es un poco intrincado, pero funciona.

Necesitará 3 archivos, archivo .h que tiene las definiciones constantes, el archivo .h y el archivo .m , .m ConstantList.h , Constants.h Constants.m , respectivamente. los contenidos de Constants.h son simplemente:

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

y el archivo Constants.m ve así:

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

Finalmente, el archivo ConstantList.h tiene las declaraciones reales y eso es todo:

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

Un par de cosas a anotar:

  1. Tuve que redefinir la macro en el archivo .m después de #undef para que se utilizara la macro.

  2. También tuve que usar #include lugar de #import para que esto funcione correctamente y evitar que el comstackdor vea los valores precomstackdos previamente.

  3. Esto requerirá una recomstackción de su PCH (y probablemente de todo el proyecto) cada vez que se modifiquen los valores, que no es el caso si están separados (y duplicados) de forma normal.

Espero que sea útil para alguien.

Yo mismo tengo un encabezado dedicado a declarar NSStrings constantes utilizados para preferencias como esta:

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

Luego los declara en el archivo .m adjunto:

 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"; 

Este enfoque me ha servido bien.

Editar: Tenga en cuenta que esto funciona mejor si las cadenas se utilizan en varios archivos. Si solo lo usa un archivo, puede hacer #define kNSStringConstant @"Constant NSString" en el archivo .m que usa la cadena.

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

Como dijo Abizer, podrías ponerlo en el archivo PCH. Otra forma que no es tan sucia es crear un archivo de inclusión para todas sus claves y luego incluirlo en el archivo en el que está utilizando las claves, o incluirlo en el PCH. Con ellos en su propio archivo de inclusión, eso al menos le da un lugar para buscar y definir todas estas constantes.

Si quieres algo como constantes globales; una manera rápida y sucia es poner las declaraciones constantes en el archivo pch .

Uso una clase singleton, para poder simular la clase y cambiar las constantes si es necesario para probar. La clase de constantes se ve así:

 #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 

Y se usa así (observe el uso de una abreviatura para las constantes c – guarda escribiendo [[Constants alloc] init] cada 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 

Intenta usar un método de clase:

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

Lo uso a veces

Si le gusta la constante de espacio de nombres, puede aprovechar struct, Friday Q & A 2011-08-19: Constantes y funciones de espacio de nombres

 // 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" };