Constantes en Objective-C


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

Entiendo que esta es una buena idea, porque permite cambiar fácilmente las teclas si es necesario. Además, es toda la noción de' separar tus datos de tu lógica'.

De todos modos, ¿hay una buena manera de hacer que estas constantes se definan una sola 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 usan.

Author: Peter Mortensen, 2009-02-12

13 answers

Debe crear un archivo de encabezado como

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

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

Puede incluir este archivo en cada archivo que use las constantes o en la cabecera precompilada del proyecto.

Se definen estas constantes en a .m archivo como

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

Constantes.m debe agregarse al objetivo de su aplicación / marco de trabajo para que esté vinculado al final producto.

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

 1249
Author: Barry Wark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-29 11:31:38

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

Un beneficio del segundo es que cambiar el valor de una constante no causa una reconstrucción de todo su programa.

 264
Author: Andrew Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-11 22:42:14

También hay una cosa que mencionar. Si necesita una constante no global, debe usar la 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 de @QuinnTaylor: las variables estáticas son visibles dentro de una unidad de compilación . Por lo general, esto es una sola.archivo m (como en este ejemplo), pero puede morderte si lo declaras en un encabezado que se incluye en otro lugar, ya que obtendrá errores de enlazador después de la compilación

 182
Author: kompozer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:26

La respuesta aceptada (y correcta) dice que "puedes incluir esta [Constantes.h] archivo... en el encabezado pre-compilado para el proyecto."

Como novato, tuve dificultades para hacer esto sin más explicación here aquí está cómo: En su YourAppNameHere-Prefix.archivo pch (este es el nombre predeterminado para el encabezado precompilado en Xcode), importa tus constantes.h dentro del bloque #ifdef __OBJC__ .

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
  #import "Constants.h"
#endif

También tenga en cuenta que las Constantes.h y Constantes.m archivos deben contener absolutamente nada más en ellos, excepto lo que se describe en la respuesta. (Sin interfaz ni implementación).

 117
Author: Victor Van Hee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-31 17:56:33

Generalmente estoy usando la forma publicada por Barry Wark y Rahul Gupta.

Aunque, no me gusta repetir las mismas palabras en ambos .h y .archivo 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 alguna maquinaria de preprocesador C. Permítanme explicar 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 .h.m par donde quiero definir el constante hago lo siguiente:

// myfile.h
#import <StringConsts.h>

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 .solo archivo h.

 50
Author: Krizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-06 01:17:37

Una ligera modificación de la sugerencia de @Krizz, para que funcione correctamente si el archivo de cabecera de constantes se va a incluir en el PCH, lo cual es bastante normal. Dado que el original se importa en el PCH, no lo recargará en el archivo .m y por lo tanto no obtendrá símbolos y el enlazador no estará contento.

Sin embargo, la siguiente modificación permite que funcione. Es un poco complicado, pero funciona.

Necesitará 3 archivos, .h archivo que tiene la constante definiciones, el archivo .h y el archivo .m, usaré ConstantList.h, Constants.h y Constants.m, respectivamente. el contenido de Constants.h es simplemente:

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

Y el archivo Constants.m se parece a:

// 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 en él y eso es todo:{[18]]}

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

Un par de cosas a tener en cuenta:

  1. Tuve que redefinir la macro en el archivo .m después de #undefing para la macro que se utilizará.

  2. También tuve para usar #include en lugar de #import para que esto funcione correctamente y evite que el compilador vea los valores precompilados previamente.

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

Espero que sea útil para alguien.

 25
Author: Scott Little,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-03 00:03:57

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

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

Luego declararlos en el acompañamiento .archivo m:

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 un archivo lo usa, solo puede hacer #define kNSStringConstant @"Constant NSString" en el .m archivo que utiliza la cadena.

 25
Author: MaddTheSane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 04:22:08
// Prefs.h
extern NSString * const RAHUL;

// Prefs.m
NSString * const RAHUL = @"rahul";
 14
Author: rahul gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-28 16:19:44

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

 12
Author: Grant Limberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-11 22:05:55

Si desea algo como constantes globales, una forma rápida y sucia es poner las declaraciones de constantes en el archivo pch.

 11
Author: Abizern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-07 09:04:36

Utilizo una clase singleton, para que pueda simular la clase y cambiar las constantes si es necesario para la prueba. La clase de constantes se ve así:

#import <Foundation/Foundation.h>

@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í (tenga en cuenta el uso de una taquigrafía 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
 7
Author: Howard Lovatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-16 00:00:35

Intente usar un método de clase:

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

Lo uso a veces.

 7
Author: groumpf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-31 17:55:33

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"
};
 7
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-12 11:54:11