2009-06-29 8 views
16

Quiero definir una constante en el objetivo-c.Definición de una constante en el objetivo-c

Yo antes tenía la siguiente función:

+(NSString *) getDocumentsDir { 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); 
    NSString *documentsDir = [paths objectAtIndex: 0]; 
    paths = nil; 
    return documentsDir; 
} 

me gustaría definir una constante "Documents_Dir" sólo una vez - cuando la función se llama y después de que para acceder a un valor previamente creada.

He intentado el siguiente código, que no funcionaba:

#define getDocumentsDir \ 
{ \ 
#ifdef Documents_Dir \ 
return Documents_Dir; \ 
#else \ 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); \ 
NSString *documentsDir = [paths objectAtIndex: 0]; \ 
#define Documents_Dir [paths objectAtIndex: 0]; \ 
paths = nil; \ 
return Documents_Dir; \ 
#endif \ 
} \ 

yo no soy fuerte con las directivas de precompilador, por lo que cualquier ayuda será apreciada.

Respuesta

34

Preludio: Vale la pena entender la diferencia entre las directivas del precompilador y las constantes verdaderas. A #define hace un reemplazo de texto antes de que el compilador construya el código. Esto funciona muy bien para constantes numéricas y typedefs, pero no siempre es la mejor idea para llamadas de función o método. Estoy operando bajo la suposición de que realmente quieres una constante verdadera, lo que significa que el código para crear la ruta de búsqueda solo debe ejecutarse una vez.


En el archivo MyClass.m, definir la variable y rellenarla en un método +initialize así:

static NSArray *documentsDir; 

@implementation MyClass 

+ (void) initialize { 
    if (documentsDir == nil) { 
     documentsDir = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES) lastObject] retain]; 
    } 
} 

... 

@end 

El static modificador hace que sea visible sólo dentro de la compilación unidad donde está declarado. Para una constante simple, esto es todo lo que necesitas.

Si la clase tiene subclases, +initialize se llama una vez para cada subclase (por defecto), por lo que querrá comprobar si documentsDir es nil antes de asignar a la misma, por lo que no perder memoria. (O, como señala Peter Lewis, puede verificar si la clase que se está inicializando actualmente es MyClass, usando == o el método -isMemberOfClass:). Si las subclases también necesitan acceder a la constante directamente, necesitaría pre-declarar la variable como extern en archivo MyClass.h (que incluyen las clases hijas):

extern NSArray *documentsDir; 

@interface MyClass : NSObject 
... 
@end 

Si pre-declara la variable como extern, debe quitar la palabra clave static de la definición para evitar la compilación errores Esto es necesario para que la variable pueda abarcar múltiples unidades de compilación. (Ah, las alegrías de la C ...)

Nota: En código de Objective-C, la mejor manera de declarar algo tan extern es utilizar OBJC_EXPORT (un #define declarados en <objc/objc-api.h>), que se establece en función sobre si estás usando C++ o no. Simplemente reemplace extern con OBJC_EXPORT y listo.


Editar: Me acaba de pasar sobre un related SO question.

+0

Gracias, pero cuando voy a construir mi proyecto, me sale el siguiente aviso: 'SearchPath' definido pero no se utiliza. La advertencia aparece en todos los archivos, excepto en aquellos en los que se usa directamente. El archivo con una constante definida se incluye en los encabezados precolombrados. ¿Hay alguna manera de deshacerse de esta advertencia? Gracias. –

+0

Eso es porque el símbolo se está importando en varios archivos. Como el archivo en cuestión se está incluyendo en muchos otros archivos, use mi orientación sobre las subclases: declare la variable como extern en el encabezado, luego elija solo un lugar (en un archivo .m) para declararlo como estático, y uno (posiblemente diferente) lugar para inicializarlo. Si ya está haciendo eso y el error persiste, prefija la declaración de la variable con __attribute __ ((no utilizado)) para indicar al compilador que suprima las advertencias sobre los símbolos no utilizados. (Personalmente uso #define UNUSED __attribute __ ((sin usar)) como abreviación para esto. –

+0

Tenga en cuenta que si MyClass está subclasificado, se llamará a la inicialización varias veces, por lo que si va a utilizar initialize, necesitará usar: if (self == [Clase MyClass]) { searchPath == ...; } –

12

La solución más fácil es simplemente cambiar las rutas a ser una variable estática y evalutate sólo una vez, así:

+(NSString *) getDocumentsDir { 
    static NSString *documentsDir = nil; 
    if (!documentsDir) { 
     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); 
     documentsDir = [paths objectAtIndex: 0]; 
    } 
    return documentsDir; 
} 

El "estática" indica al compilador que documentsDir es efectivamente una variable global, aunque sólo accesible dentro de la función. Por lo tanto, se inicializa en cero, y la primera llamada a getDocumentsDir lo evaluará y luego otras llamadas devolverán el valor pre-evaluado.

+0

+1 Buena llamada, ¡había olvidado que puede declarar una variable estática dentro de una función o método! De hecho, esta es la manera más fácil, pero tenga en cuenta que ninguna otra unidad de compilación (subclase u otra) podrá hacer referencia al símbolo. Si solo necesita la constante en un archivo, intente con este enfoque. –

+1

Justo lo que necesitaba para poner a disposición una matriz de números sin cambios sin instanciarlo una y otra vez. Si desea usar documentsDir en sus métodos de clase, coloque "static NSString * documentsDir = nil;" fuera del método de clase sin embargo. –

1

Pequeño optimización con respecto código Peter N Lewis:

-(NSString *) documentsDir { 
    static NSString *documentsDir = nil; 
    return documentsDir ?: (documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]); 
} 
+0

¿Es esto realmente una optimización? Quiero decir, ¿es esto más rápido de alguna manera? (Parece que sería exactamente lo mismo para mí.) ¿Estás seguro de que no son solo menos personajes? Si este último, estoy en desacuerdo con este método sobre el principio de legibilidad. Es innecesariamente confuso, IMO. – livingtech

+0

No es más rápido, pero más seguro: cambié "objectAtIndex: 0" por "lastObject". –

Cuestiones relacionadas