#define vs const en Objective-C

81

Soy nuevo en Objective-C y tengo algunas preguntas sobre constla directiva de preprocesamiento #define.

Primero, descubrí que no es posible definir el tipo de constante usando #define. ¿Porqué es eso?

En segundo lugar, ¿tiene alguna ventaja utilizar uno de ellos sobre el otro?

Finalmente, ¿de qué manera es más eficiente y / o más segura?

ObjectiveCoder
fuente

Respuestas:

107

Primero, descubrí que no es posible definir el tipo de constante usando #define, ¿por qué?

¿Por qué es qué? No es verdad:

#define MY_INT_CONSTANT ((int) 12345)

En segundo lugar, ¿tiene alguna ventaja utilizar uno de ellos sobre el otro?

Si. #definedefine una macro que se reemplaza incluso antes de que comience la compilación. constsimplemente modifica una variable para que el compilador marque un error si intenta cambiarla. Hay contextos en los que puedes usar a #definepero no puedes usar a const(aunque estoy luchando por encontrar uno usando el último clang). En teoría, un constocupa espacio en el ejecutable y requiere una referencia a la memoria, pero en la práctica esto es insignificante y puede ser optimizado por el compilador.

consts son mucho más amigables con el compilador y el depurador que #defines. En la mayoría de los casos, este es el punto primordial que debe considerar al tomar una decisión sobre cuál usar.

Solo piensa en un contexto en el que puedas usar #definepero no const. Si tiene una constante que desea usar en muchos .carchivos, #definesimplemente péguela en un encabezado. Con a consttienes que tener una definición en un archivo C y

// in a C file
const int MY_INT_CONST = 12345;

// in a header
extern const int MY_INT_CONST;

en un encabezado. MY_INT_CONSTno se puede usar como el tamaño de una matriz de alcance estático o global en ningún archivo C, excepto en el que está definido.

Sin embargo, para constantes enteras, puede usar un enum. De hecho, eso es lo que hace Apple casi invariablemente. Esto tiene todas las ventajas de #defines y consts, pero solo funciona para constantes enteras.

// In a header
enum
{
    MY_INT_CONST = 12345,
};

Finalmente, ¿de qué manera es más eficiente y / o más segura?

#definees más eficiente en teoría aunque, como dije, los compiladores modernos probablemente aseguran que haya poca diferencia. #definees más seguro porque siempre es un error del compilador intentar asignarle

#define FOO 5

// ....

FOO = 6;   // Always a syntax error

constSe puede engañar a s para que se asignen, aunque el compilador puede emitir advertencias:

const int FOO = 5;

// ...

(int) FOO = 6;     // Can make this compile

Dependiendo de la plataforma, la asignación aún puede fallar en tiempo de ejecución si la constante se coloca en un segmento de solo lectura y su comportamiento oficialmente indefinido de acuerdo con el estándar C.

Personalmente, para constantes enteras, siempre uso enums para constantes de otros tipos, uso a constmenos que tenga una muy buena razón para no hacerlo.

JeremyP
fuente
Sé que es antiguo, pero una instancia en la que no puede usar una constante que podría usar una definición es "#define MY_NSNUM_CONSTANT @ 5" que no se pudo hacer usando "NSNumber * const MY_NSNUM_CONSTANT = @ 5"
shortstuffsushi
Yo diría que #define ocupa más espacio en el ejecutable que const. Una constante se almacena una vez, pero una #define se multiplica cada vez que la usa porque es solo un reemplazo de texto. Pero como la diferencia es insignificante, estoy siendo innecesariamente pedante.
Ryan Ballantyne
@RyanBallantyne Creo que podría ser adecuado para cosas como constantes de cadena, pero no para constantes enteras porque incluso si almacena la constante en un lugar, para acceder a ella necesita su dirección, que es al menos tan grande como un archivo int. Sin embargo, me sorprendería mucho si hiciera alguna diferencia en un compilador moderno.
JeremyP
16

Desde un codificador C:

A constes simplemente una variable cuyo contenido no se puede cambiar.

#define name value, sin embargo, es un comando de preprocesador que reemplaza todas las instancias de namewith value.

Por ejemplo, si usted #define defTest 5, todas las instancias de defTesten su código serán reemplazadas por 5cuando compile.

MegaNairda
fuente
11

Es importante comprender la diferencia entre las instrucciones #define y const que no están destinadas a las mismas cosas.

const

constse utiliza para generar un objeto del tipo solicitado que será, una vez inicializado, constante. Significa que es un objeto en la memoria del programa y se puede utilizar como de solo lectura. El objeto se genera cada vez que se inicia el programa.

#define

#definese utiliza para facilitar la legibilidad del código y futuras modificaciones. Cuando usa una definición, solo enmascara un valor detrás de un nombre. Por lo tanto, al trabajar con un rectángulo, puede definir el ancho y el alto con los valores correspondientes. Luego, en el código, será más fácil de leer ya que en lugar de números habrá nombres.

Si más tarde decide cambiar el valor del ancho, solo tendrá que cambiarlo en la definición en lugar de una búsqueda / reemplazo aburrida y peligrosa en todo el archivo. Al compilar, el preprocesador reemplazará todo el nombre definido por los valores en el código. Por lo tanto, no hay tiempo que perder usándolos.

fBurgués
fuente
7

Además de los comentarios de otras personas, los errores de uso #defineson notoriamente difíciles de depurar ya que el preprocesador se apodera de ellos antes que el compilador.

Ed Heal
fuente
3

Dado que las directivas de preprocesador están mal vistas, sugiero usar un archivo const. No puede especificar un tipo con un preprocesador porque una directiva de preprocesador se resuelve antes de la compilación. Bueno, puedes, pero algo como:

#define DEFINE_INT(name,value) const int name = value;

y usarlo como

DEFINE_INT(x,42) 

que sería visto por el compilador como

const int x = 42;

Primero, descubrí que no es posible definir el tipo de constante usando #define, ¿por qué?

Puedes ver mi primer fragmento.

En segundo lugar, ¿tiene alguna ventaja utilizar uno de ellos sobre el otro?

En general, tener una constdirectiva en lugar de un preprocesador ayuda con la depuración, no tanto en este caso (pero aún lo hace).

Finalmente, ¿de qué manera es más eficiente y / o más segura?

Ambos son igualmente eficientes. Yo diría que la macro puede ser potencialmente más segura ya que no se puede cambiar durante el tiempo de ejecución, mientras que una variable sí.

Luchian Grigore
fuente
¿Por qué simplemente escribir const ...en lugar de usar una macro?
Ed Heal
1
@EdHeal no lo hace. Solo decía que se puede en respuesta a "Encontré que no es posible definir el tipo de constante usando #define, ¿por qué?"
Luchian Grigore
1
> pre-processor directives are frowned upon[cita requerida]
Ben Leggiero
Creo que puedes usar un tipo como este: #define myValue ((double) 2). Según tengo entendido, el preprocesador simplemente reemplazará "myValue" con lo que viene después en la declaración de definición, incluida la información de tipo.
vomako
1

He usado #define antes para ayudar a crear más métodos a partir de un método, como si tuviera algo como.

 // This method takes up to 4 numbers, we don't care what the method does with these numbers.
 void doSomeCalculationWithMultipleNumbers:(NSNumber *)num1 Number2:(NSNumber *)num2 Number3:(NSNumber *)num23 Number3:(NSNumber *)num3;

Pero también tengo un método que solo toma 3 números y 2 números, así que en lugar de escribir dos métodos nuevos, usaré el mismo usando el #define, así.

 #define doCalculationWithFourNumbers(num1, num2, num3, num4) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), (num4))

 #define doCalculationWithThreeNumbers(num1, num2, num3) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), nil)

 #define doCalculationWithTwoNumbers(num1, num2) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), nil, nil)

Creo que es algo muy bueno tener, sé que puedes ir directamente al método y poner nil en los espacios que no quieres, pero si estás construyendo una biblioteca es muy útil. También así es como

     NSLocalizedString(<#key#>, <#comment#>)
     NSLocalizedStringFromTable(<#key#>, <#tbl#>, <#comment#>)
     NSLocalizedStringFromTableInBundle(<#key#>, <#tbl#>, <#bundle#>, <#comment#>)

están hechos.

Mientras que no creo que puedas hacer esto con constantes. Pero las constantes tienen sus ventajas sobre #define, ya que no puede especificar un tipo con un #define porque es una directiva de preprocesador que se resuelve antes de la compilación, y si obtiene un error con #define, son más difíciles de depurar entonces constantes. Ambos tienen sus ventajas y desventajas, pero yo diría que todo depende del programador al que decidas usar. He escrito una biblioteca con ambos usando #define para hacer lo que he mostrado y constantes para declarar variables constantes en las que necesito especificar un tipo.

Popeye
fuente