2010-01-19 8 views
44

estoy un poco confundido en cuanto a cuándo es mejor utilizar:de usar cadena estática vs #define

static NSString *AppQuitGracefullyKey = @"AppQuitGracefully"; 

en lugar de

#define AppQuitGracefullyKey @"AppQuitGracefully" 

que he visto este tipo de preguntas para C o C++, y creo que lo que es diferente aquí es que esto es específicamente para Objective C, que utiliza un objeto, y en un dispositivo como el iPhone, puede haber problemas de pila, espacio de código o memoria que aún no entiendo.

Un uso sería:

appQuitGracefully = [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey]; 

O es sólo una cuestión de estilo?

Gracias.

Respuesta

58

Si usa una estática, el compilador insertará exactamente una copia de la cadena en su binario y simplemente pasará los punteros a esa cadena, dando como resultado binarios más compactos. Si usa #define, habrá una copia separada de la cadena almacenada en la fuente en cada uso. La combinación constante de cadenas manejará muchos de los dups pero hará que el enlazador trabaje más sin ninguna razón.

+9

La unión constante de cadenas es el trabajo del compilador, no del enlazador. – kennytm

+1

Esto no es correcto. Objective-c usa [String Interning] (https://en.wikipedia.org/wiki/String_interning) como se describe [aquí] (http://nshipster.com/equality/). '# define' does _not_ store una copia de la cadena para cada uso. Y @kennytm tiene razón, el compilador hace eso, no el enlazador –

+1

@ AndréFratelli De hecho, la fusión constante de cadenas es una optimización de enlazadores, y se implementa mediante el enlazador estático en OS X/macOS para C y CF/NSStrings . No se hace mediante el uso de cadenas, y el compilador no lo hace (aunque el compilador puede unir cadenas antes de escribir el archivo objeto que está generando). Puede ver el código que hace esto en [las fuentes del enlazador] (http://opensource.apple.com/source/ld64/ld64-264.3.102/). Entonces, no, kennytm no está bien; realmente es el engarce. – alastair

3

Uso static cuando necesito exportar símbolos NSString desde una biblioteca o un marco. Uso #define cuando necesito una cadena en muchos lugares que puedo cambiar fácilmente. De todos modos, el compilador y el enlazador se encargarán de las optimizaciones.

14

Ver "static const" vs "#define" vs "enum". La principal ventaja de static es el tipo de seguridad.

Aparte de eso, el enfoque #define introduce una flexibilidad de concatenación de cadenas en línea que no se puede hacer con variables estáticas, p.

#define ROOT_PATH @"/System/Library/Frameworks" 
[[NSBundle bundleWithPath:[email protected]"/UIKit.framework"] load]; 

pero esto probablemente no es un buen estilo :).

+33

Estoy tan sorprendido. No tenía idea '@" una cadena "@" otra cadena "' era válida. –

+0

De su comentario a la respuesta de cdespinosa, ¿significa que usar #define no producirá duplicación? – user523234

+0

@ user523234 lo hace. El compilador utiliza [String Interning] (https://en.wikipedia.org/wiki/String_interning) –

4

Después de hacer algunas de búsqueda (this pregunta/respuesta entre otras cosas) creo que es importante decir que en cualquier momento cuando se utiliza se crea cadena literal @"AppQuitGracefully" cadena constante, y no importa cuántas veces lo usa apuntará al mismo objeto.

Así que creo (y yo me disculpe si me equivoco) que esta frase en la respuesta anterior es incorrecto: If you use a #define, there will be a separate copy of the string stored in the source on each use.

3

USO #define:

no puede depurar el valor de identificador

trabajo con #define y otras macros es un trabajo de pre-procesador, al llegar a Build/Run primera se puede procesar previamente el código fuente, que trabajará con todas las macros (comenzando con el símbolo #),

Supongamos, que ha creado,

#define LanguageTypeEnglish @"en" 

y se utiliza esto en 2 lugares en su código.

NSString *language = LanguageTypeEnglish; 
NSString *languageCode = LanguageTypeEnglish; 

que reemplazará "LanguageTypeEnglish" con @"en", en todos los lugares. De modo que se generarán 2 copias de @"en". es decir

NSString *language = @"en"; 
NSString *languageCode = @"en"; 

Recuerde, hasta que este proceso, compilador no está en la imagen.

Después de preprocesamiento todas las macros, compilador viene en la imagen, y se obtendrá el código de entrada de este tipo,

NSString *language = @"en"; 
NSString *languageCode = @"en"; 

y compilarlo.

USANDO estática:

respeta alcance y es de tipo seguro. puede depurar el valor de identificador

Durante el proceso de compilación si el compilador encuentra,

static NSString *LanguageTypeRussian = @"ru"; 

entonces se comprobará si la variable con el mismo nombre almacenado previamente, si es así, sólo pasar el puntero de esa variable, si no, creará esa variable y pasará su puntero, la próxima vez solo pasará el puntero de la misma.

Por lo tanto, utilizando estático, solo se genera una copia de la variable dentro del alcance.

4

En realidad, yo no recomendaría ninguno, debería usar extern en su lugar. Objective-C ya define FOUNDATION_EXPORT que es more portable que extern, por lo que un NSString instancia global sería algo como esto:

.h

FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey; 

.m

NSString * const AppQuitGracefullyKey = @"AppQuitGracefully"; 

por lo general ponen estos en archivos de declaración (como MyProjectDecl.h) e importar cada vez que lo necesite.

Hay algunas diferencias con respecto a estos enfoques:

  • #define tiene varias desventajas, tales como no segura tipo. Es cierto que hay soluciones para eso (como #define ((int)1)), pero ¿cuál es el punto? Y, además, hay desventajas de depuración para ese enfoque. Los compiladores prefieren constantes. Ver discusión this.
  • estáticas globales son visible in the file they are declared.
  • externo hace que la variable visible a todos los archivos. Eso contrasta con la estática.

Las diferencias de estática y externa difieren en la visibilidad.También es notable que ninguno de estos enfoques duplica la cadena (ni siquiera #define) ya que el compilador usa String Interning para evitar eso. En this NSHipster post que acrediten:

NSString *a = @"Hello"; 
NSString *b = @"Hello"; 
BOOL wtf = (a == b); // YES 

El operador == vuelve YES sólo si las dos variables apuntan en la misma instancia. Y como pueden ver, lo hace.

La conclusión es: use FOUNDATION_EXPORT para las constantes globales. Es fácil de depurar y será visible independientemente de su proyecto.

Cuestiones relacionadas