ios objective-c c static extern

ios - const constantes contra const constantes



objective-c static (7)

He estado usando const estática en mis archivos de cabecera como tal:

static NSString * const myString = @"foo";

Pero he leído que esta no es la forma ''segura'' o correcta de hacer esto. Aparentemente, si quiero que se acceda a mis cadenas constantes desde otra clase, debería declarar la cadena en mi .h como:

extern NSString * const myString;

Luego en mi archivo .m:

NSString * const myString = @"foo";

¿Es esto correcto? Si es así, ¿cuál es la razón para no declararlo como estático directamente en mi archivo .h? Funciona perfectamente bien, y no puedo ver ningún problema de "seguridad" al respecto. Es una constante, por lo tanto, no se puede cambiar desde afuera y es algo que necesito acceder fuera de clase. ¿Lo único en lo que puedo pensar es en ocultar el valor de la cadena?


Pero he leído que este no es el "seguro" o la cautela correcta de hacer esto.

Es seguro, a menos que el programa sea multiproceso. En ese caso, no es seguro a menos que proteja la variable global con un mutex.

Sin embargo, no es correcto. NSString * const myString significa un puntero constante a datos (no constantes). Lo más probable es que desee que la variable sea constante: const NSString* myString .

Aparentemente, si quiero que se acceda a mis cadenas constantes desde otra clase, debería declarar la cadena en mi .h como: extern ...

Correcto. Tenga en cuenta que extern es aceptable para constantes. No es aceptable para las variables no constantes: las variables globales se consideran prácticas muy malas.

si es así, ¿cuál es la razón para no solo usarlo declararlo como estático directamente en mi archivo .h, ya que funciona perfectamente bien?

La única razón por la que querría declararlo como estático es porque desea limitar el alcance de la variable al archivo .c local, en otras palabras, encapsulación privada . Por lo tanto, nunca tiene sentido declarar variables estáticas en un archivo .h.

Es una const, por lo tanto, no puede ser cambiado desde el exterior.

Se puede cambiar, porque no es constante, vea mi primer comentario.

En general, no hagas cosas como esta. Todos los problemas anteriores sugieren que tiene fallas en el diseño de su programa, que deben solucionarse. Debe diseñar su programa de forma orientada a objetos, de modo que cada módulo de código sea autónomo y no sepa ni se preocupe por nada, excepto por su tarea designada.


Cuando se trata de clases de almacenamiento, estática significa una de dos cosas.

Una variable estática dentro de un método o función conserva su valor entre invocaciones.

Una variable estática declarada globalmente puede ser llamada por cualquier función o método, siempre que esas funciones aparezcan en el mismo archivo que la variable estática. Lo mismo ocurre con las funciones estáticas.

Mientras que static hace que las funciones y variables sean visibles globalmente dentro de un archivo en particular, extern las hace visibles globalmente para todos los archivos.

Cada vez que su aplicación utilice una constante de cadena con un valor no lingüístico en una interfaz pública, debe declararla como una constante de cadena externa.

El patrón es declarar una constante NSString * externa en un encabezado público y definir esa constante NSString * en la implementación.

Fuente: nshipster.com/c-storage-classes


La ventaja de la vía externa se describe en otras respuestas.

Una posible ventaja de la forma estática si la variable no fuera un objeto ( NSString en su caso) sino de un tipo primitivo (como un entero) es que el compilador puede optimizar el acceso a la constante:

Cuando declaras una const en tu programa,

int const x = 2;

El compilador puede optimizar esta constante al no proporcionar almacenamiento a esta variable, sino agregarla en la tabla de símbolos. Por lo tanto, la lectura posterior solo necesita direccionamiento indirecto en la tabla de símbolos en lugar de instrucciones para obtener el valor de la memoria.

Tomado de esta respuesta .


No hay ninguna razón que conozca, para declarar algo externo en Objective-C, mientras que usa Objective-C solo en su proyecto. Podría pensar en razones cuando lo mezcla con C o módulos de ensamblador, etc.

Sin embargo, extern tiene la ventaja de que la constante realmente existirá solo en todo el proyecto, si es eso lo que quiere lograr, si realmente necesita ahorrar en estos 20 o más bytes. Pero eso conlleva el riesgo de nombres en conflicto. Otras bibliotecas pueden haber declarado sus propios elementos externos con el mismo nombre. Y el enlazador los cuidaría usando el mismo espacio en la memoria, aunque pueden ser de diferentes tipos.

Y sí, la declaración extern en el encabezado debe ir acompañada de una definición correspondiente en el archivo .m. No estoy seguro, pero creo que podría asignar @ "foo" en el archivo .h ya. Incluso podría declararlo fuera de los bloques @ interfaz / @ implementación- @ end. (Nunca lo intenté yo mismo). En ese caso, la variable sería global y accesible desde cualquier lugar, incluso sin la palabra clave extern . En tiempo de compilación, el compilador se quejaría de tener acceso a ellos cuando no ve su declaración dentro de la cadena de declaraciones #include. Pero académicamente, un solo archivo .m podría contener dos o más clases (lo que claramente no aconsejo) y luego la variable sería accesible desde ambas clases aunque no pertenece a ninguna de ellas. Al final, OjbC es solo un complemento de ANSI C.

Sin embargo, no tiene sentido hacerlos estáticos. Estas constantes son estáticas de todos modos. Son constantes. El propósito de una variable estática dentro de una clase o incluso un método es que su alcance (visibilidad) está limitado a esa clase, pero solo hay una instancia en tiempo de ejecución que es compartida por todas las instancias de la clase.

Ejemplo:

@implementation AClass : NSObject static NSString *someString - (void) setString:(NSString*) aString{ someString = aString; } - (NSString*) getString (){ return someString; }

... y en otro lugar:

AClass * a = [[AClass alloc] init]; AClass * b = [[AClass alloc] init]; [a setString:@"Me"]; [b setString;@"You"]; NSLog (@"String of a: ", [a getString]);

You imprimiría a You pero no a Me

Si eso es lo que quieres, y solo entonces, usa estática.

El uso de macros de preprocesador simples (lo cual prefiero, pero aquí soy un poco viejo) tiene la desventaja de que estas cadenas se copiarían al binario cada vez que se usa la macro. Aparentemente esa no es una opción para ti de todos modos porque ni siquiera los pediste. Sin embargo, para la mayoría de los usos, las macros de preprocesador en los archivos .h comúnmente compartidos harían el truco de administrar constantes en todas las clases.


Tu primera variante

static NSString * const myString = @"foo"; // In .h file, included by multiple .m files

define una variable myString localmente en cada "unidad de traducción" (en términos generales: en cada archivo fuente .m) que incluye el archivo de encabezado. Todos los objetos de cadena tienen el mismo contenido "foo", pero pueden ser objetos diferentes, por lo que el valor de myString (el puntero al objeto de cadena) puede ser diferente en cada unidad.

Tu segunda variante

extern NSString * const myString; // In .h file, included by multiple .m files NSString * const myString = @"foo"; // In one .m file only

define una única variable myString que es visible "globalmente".

Ejemplo: en una clase, usted envía una notificación con myString como objeto de usuario. En otra clase, esta notificación se recibe y el objeto de usuario se compara con myString .

En su primera variante, la comparación debe hacerse con isEqualToString: porque el envío y la clase receptora pueden tener diferentes punteros (ambos apuntan a un objeto NSString con el contenido "foo"). Por lo tanto, comparar con == puede fallar.

En su segunda variante, solo hay una variable myString , por lo que puede comparar con == .

Así que la segunda variante es más segura en el sentido de que la "cadena compartida" es el mismo objeto en cada unidad de traducción.


Usando static NSString* const myString = @"foo"; en un archivo de encabezado significa que cada unidad de traducción obtiene una variable myString separada. Creo que el enlazador puede consolidarlos, pero no contaría con eso. Eso significa que el código que compara una cadena que ha recibido usando if (someString == myString) ... podría volverse falso incluso si la persona que llama pasó myString , si la persona que llamó fue de una unidad de traducción diferente. (Por supuesto, el código debe usar -isEqualToString: lugar de == , pero con una constante de cadena debidamente declarada, esta última puede ser viable).


#define myString @"foo"

Hay un ejemplo de por qué:

Podrás concatenar cadenas en tiempo de compilación:

NSLog(@"%@", @"Say hello to " myString);

Saldrá: di hola a foo