objective-c - functions - operators objective c




Constantes en Objective-C (9)

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

Entiendo que es una buena idea, ya que permite cambiar las teclas fácilmente si es necesario. Además, es todo el concepto de 'separar sus datos de su lógica'.

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


Como dijo Abizer, podrías ponerlo en el archivo PCH. Otra forma que no está 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, que al menos te da un lugar para buscar y definir todas estas constantes.


Debe crear un archivo de encabezado 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 mixtos C / C ++ o en otras plataformas)

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

Estas constantes se definen en un archivo .m como

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

Se deben agregar constantes.m al objetivo de su aplicación / marco para que esté vinculado al producto final.

La ventaja de usar constantes de cadena en lugar de constantes de #define 'd es que puede probar la igualdad mediante 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).


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

Una de las ventajas de la segunda es que cambiar el valor de una constante no causa una reconstrucción de todo el programa.


La respuesta aceptada (y correcta) dice que "puede incluir este archivo [Constants.h] ... en el encabezado precompilado del proyecto".

Como novato, tuve dificultades para hacer esto sin más explicaciones: a continuación se explica cómo: en su archivo YourAppNameHere-Prefix.pch (este es el nombre predeterminado para el encabezado precompilado en Xcode), importe su Constants.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 los archivos Constants.h y Constants.m no deben contener absolutamente nada más, excepto lo que se describe en la respuesta aceptada. (Sin interfaz o implementación).


Si quieres algo como constantes globales; Una forma rápida y sucia es colocar las declaraciones constantes en el archivo pch .


También hay una cosa que 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 constante no es visible fuera del archivo.

Corrección menor por @QuinnTaylor : las variables estáticas son visibles dentro de una unidad de compilación . Por lo general, este es un solo archivo .m (como en este ejemplo), pero puede morderlo si lo declara en un encabezado que se incluye en otro lugar, ya que obtendrá errores del enlazador después de la compilación


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

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

Necesitará 3 archivos, el archivo .h que tiene las definiciones constantes, el archivo .h y el archivo .m , .m ConstantList.h , Constants.h y 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 como:

// 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:

// 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 ing para que se use la macro.

  2. También tuve que usar #include lugar de #import para que esto funcione correctamente y evitar 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 modifique algún valor, lo cual no es el caso si se separan (y duplican) de manera normal.

Espero que sea de ayuda para alguien.


Utilizo una clase de singleton, para poder burlarme de 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 de esta manera (tenga en cuenta el uso de una abreviatura para las constantes c - se 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

// Prefs.h
extern NSString * const RAHUL;

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






constants