Error del compilador: "el elemento inicializador no es una constante en tiempo de compilación"

76

Al compilar este código, aparece el error "el elemento inicializador no es una constante en tiempo de compilación". ¿Alguien puede explicar por qué?

#import "PreferencesController.h"

@implementation PreferencesController

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}


NSImage* imageSegment = [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];//error here
Mella
fuente

Respuestas:

105

Cuando define una variable fuera del alcance de una función, el valor de esa variable se escribe realmente en su archivo ejecutable. Esto significa que solo puede usar un valor constante. Como no sabe todo sobre el entorno de ejecución en el momento de la compilación (qué clases están disponibles, cuál es su estructura, etc.), no puede crear objetos objetivos c hasta el tiempo de ejecución, con la excepción de las cadenas constantes, a las que se les asigna un valor específico. estructura y garantizado para permanecer de esa manera. Lo que debe hacer es inicializar la variable a cero y usarla +initializepara crear su imagen. initializees un método de clase que se llamará antes de que se llame a cualquier otro método en su clase.

Ejemplo:

NSImage *imageSegment = nil;
+ (void)initialize {
    if(!imageSegment)
        imageSegment = [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];
}
- (id)init {
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}
ughoavgfhw
fuente
4
Otra opción es usar una función con __attribute__ ((constructor)).
12
Otra opción más es cambiar el tipo de archivo fuente de Objective-C a Objective-C ++ (o cambiarle el nombre de .m a .mm, que tiene el mismo efecto). En C ++, estos inicializadores no necesitan ser valores constantes en tiempo de compilación, y el código original funcionaría bien.
Soy
1
¿Hay alguna forma de declararlo constde esa manera? ¿Es decir, una variable que solo se puede configurar una vez y nunca más?
devios1
¿Qué pasa si quiero incluir una constante de varios archivos? ¿Es posible escribir la función de inicialización varias veces, es decir. ¿Puede estar compuesto por varias partes?
Vladimir Despotovic
@VladimirDespotovic No, no se puede dividir. Puede tener un +initializemétodo para diferentes clases, o podría llamar a funciones desde otros archivos, pero debe tener mucho cuidado con cosas como esta. Realmente es mejor evitarlo si es posible.
ughoavgfhw
23

Una variable global debe inicializarse a un valor constante, como 4o 0.0o @"constant string"o nil. Un constructor de objetos, como init, no devuelve un valor constante.

Si desea tener una variable global, debe inicializarla nily luego devolverla usando un método de clase:

NSImage *segment = nil;

+ (NSImage *)imageSegment
{
    if (segment == nil) segment = [[NSImage alloc] initWithContentsOfFile:@"/user/asd.jpg"];
    return segment;
}
mipadi
fuente
¿Por qué podría tener NSString estático - usando la sintaxis @ "myString" si NSString es un objeto?
Paul Brewczynski
4
@bluesm: El compilador de Objective-C usa algunos trucos para crear una cadena que se trata como un valor constante.
mipadi
11

Porque le pide al compilador que inicialice una variable estática con código que es inherentemente dinámico.

bbum
fuente
6

La razón es que está definiendo su parte imageSegmentexterna de una función en su código fuente (variable estática).

En tales casos, la inicialización no puede incluir la ejecución de código, como llamar a una función o asignar una clase. El inicializador debe ser una constante cuyo valor se conozca en el momento de la compilación.

Luego puede inicializar su variable estática dentro de su initmétodo (si pospone su declaración a init).

sergio
fuente
1
La clase de almacenamiento estático es el problema. El problema seguiría ocurriendo incluso si estuviera dentro de una función (y se declarara estática).
jww
@noloader: Nunca he dicho lo contrario ... :-) solo, el caso específico de almacenamiento estático en el caso del OP era una variable global ... (entre paréntesis, el concepto: todo depende de quién sea el lector si es mejor comenzar con el concepto o el caso concreto).
sergio
4

Ciertamente puede #definir una macro como se muestra a continuación. El compilador reemplazará "IMAGE_SEGMENT" con su valor antes de la compilación. Si bien logrará definir una búsqueda global para su matriz, no es lo mismo que una variable global. Cuando se expande la macro, funciona igual que el código en línea, por lo que se crea una nueva imagen cada vez. Entonces, si tiene cuidado con el lugar donde usa la macro, entonces habría logrado efectivamente crear una variable global.

#define IMAGE_SEGMENT [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];

Luego úselo donde lo necesite como se muestra a continuación. Cada vez que se ejecuta el siguiente código, se crea un nuevo objeto con un nuevo puntero de memoria.

imageSegment = IMAGE_SEGMENT
Kris Subramanian
fuente