Compiler error: "initializer element is not a compile-time constant"
Asked Answered
R

7

87

When compiling this code, I get the error "initializer element is not a compile-time constant". Can anyone explain why?

#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
Romito answered 26/5, 2011 at 18:7 Comment(0)
B
115

When you define a variable outside the scope of a function, that variable's value is actually written into your executable file. This means you can only use a constant value. Since you don't know everything about the runtime environment at compile time (which classes are available, what is their structure, etc.), you cannot create objective c objects until runtime, with the exception of constant strings, which are given a specific structure and guaranteed to stay that way. What you should do is initialize the variable to nil and use +initialize to create your image. initialize is a class method which will be called before any other method is called on your class.

Example:

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;
}
Bourse answered 26/5, 2011 at 18:22 Comment(6)
Another option is to use a function with __attribute__ ((constructor)).Harmattan
Yet another option is to switch the type of your source file from Objective-C to Objective-C++ (or rename it from .m to .mm, which has the same effect). In C++, such initializers don't need to be compile-time constant values, and the original code would work just fine.Selfrealization
Is there any way to declare a const in such a fashion? I.e. a variable that can only be set once and never again?Gwenora
What if I want to include a constant from several files. Is it possible to write initialize function multiple times, ie. can it be comprised of several parts?Cardamom
@VladimirDespotovic No it can't be split. You can have an +initialize method for different classes, or it could call functions from other files, but you have to be really careful with things like this. It's really best to avoid it if possible.Bourse
How to properly define a dictionary then, as a constant?Cardamom
S
26

A global variable has to be initialized to a constant value, like 4 or 0.0 or @"constant string" or nil. A object constructor, such as init, does not return a constant value.

If you want to have a global variable, you should initialize it to nil and then return it using a class method:

NSImage *segment = nil;

+ (NSImage *)imageSegment
{
    if (segment == nil) segment = [[NSImage alloc] initWithContentsOfFile:@"/user/asd.jpg"];
    return segment;
}
Slipon answered 26/5, 2011 at 18:14 Comment(2)
Why you could have static NSString - using @"myString" syntax if NSString is object ?Pruter
@bluesm: The Objective-C compiler uses some tricks to create a string that is treated as a constant value.Slipon
K
11

Because you are asking the compiler to initialize a static variable with code that is inherently dynamic.

Kloman answered 26/5, 2011 at 18:14 Comment(0)
J
6

The reason is that your are defining your imageSegment outside of a function in your source code (static variable).

In such cases, the initialization cannot include execution of code, like calling a function or allocation a class. Initializer must be a constant whose value is known at compile time.

You can then initialize your static variable inside of your init method (if you postpone its declaration to init).

Jinn answered 26/5, 2011 at 18:15 Comment(2)
Static storage class is the problem. The issue would still occur even if it were inside a function (and declared static).Stempien
@noloader: I have never stated the contrary... :-) only, the specific case of static storage in the OP's case was a global variable... (in parenthesis, the concept - it all depends on who the reader is whether it is better starting with the concept or the concrete case).Jinn
C
4

You can certainly #define a macro as shown below. The compiler will replace "IMAGE_SEGMENT" with its value before compilation. While you will achieve defining a global lookup for your array, it is not the same as a global variable. When the macro is expanded, it works just like inline code and so a new image is created each time. So if you are careful in where you use the macro, then you would have effectively achieved creating a global variable.

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

Then use it where you need it as shown below. Each time the below code is executed, a new object is created with a new memory pointer.

imageSegment = IMAGE_SEGMENT
Conciliar answered 2/6, 2015 at 15:38 Comment(0)
L
0

You can use the static singleton approach with dispatch_once:

#define STATIC_VAR(type, code) ^type() { \
    static type object; \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        object = code; \
    }); \
    return object; \
};

#define let __auto_type const

let imageSegment = STATIC_VAR(UIImage*, [[UIImage alloc] initWithContentsOfFile:@"image.jpg"]);
let imageRect = STATIC_VAR(CGRect, CGRectMake(0, 0, 100, 100));

// How to use:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.imageView.image = imageSegment();
    self.imageView.frame = imageRect();
}

It is thread safe, works lazily with any type and makes only a single instance.

Leaves answered 12/8, 2021 at 10:10 Comment(0)
E
0

I got this error while practicing C language, my code that I was trying to run was this

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    char *name;
    int age;
} person;

person *p = (person *)malloc(sizeof(person));

and I realized while reading answers, that in C, I should have main function, which I forgot to use, so put the person code in main function, thus removing the error as follows

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    char *name;
    int age;
} person;

int main()
{

    person *p = (person *)malloc(sizeof(person));
    return 0;
}
Eastnortheast answered 18/3, 2022 at 7:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.