Ошибка компилятора: «элемент инициализатора не является константой времени компиляции»

#objective-c #cocoa #initialization #compiler-errors #global-variables

#objective-c #cocoa #инициализация #ошибки компилятора #глобальные переменные

Вопрос:

При компиляции этого кода я получаю ошибку «элемент инициализатора не является константой времени компиляции». Кто-нибудь может объяснить, почему?

 #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
  

Ответ №1:

Когда вы определяете переменную вне области видимости функции, значение этой переменной фактически записывается в ваш исполняемый файл. Это означает, что вы можете использовать только постоянное значение. Поскольку вы не знаете всего о среде выполнения во время компиляции (какие классы доступны, какова их структура и т.д.), Вы не можете создавать объекты objective c до времени выполнения, за исключением постоянных строк, которым присваивается определенная структура и гарантируется, что они останутся такими. Что вам следует сделать, так это инициализировать переменную равным nil и использовать initialize для создания вашего образа. initialize — это метод класса, который будет вызван перед вызовом любого другого метода в вашем классе.

Пример:

 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;
}
  

Комментарии:

1. Другой вариант — использовать функцию с __attribute__ ((constructor)) .

2. Еще одним вариантом является переключение типа вашего исходного файла с Objective-C на Objective-C (или переименование его из .m в .mm, что имеет тот же эффект). В C такие инициализаторы не обязательно должны быть значениями константы времени компиляции, и исходный код будет работать просто отлично.

3. Есть ли какой-либо способ объявить const таким образом? Т.Е. переменную, которую можно установить только один раз и никогда больше?

4. Что, если я хочу включить константу из нескольких файлов. Возможно ли написать функцию инициализации несколько раз, т.Е.. может ли он состоять из нескольких частей?

5. @VladimirDespotovic Нет, его нельзя разделить. У вас может быть initialize метод для разных классов, или он может вызывать функции из других файлов, но вы должны быть действительно осторожны с подобными вещами. Действительно, лучше избегать этого, если это возможно.

Ответ №2:

Глобальная переменная должна быть инициализирована постоянным значением, таким как 4 или 0.0 , или @"constant string" или nil . Конструктор объекта, такой как init , не возвращает постоянное значение.

Если вы хотите иметь глобальную переменную, вы должны инициализировать ее в nil , а затем вернуть ее с помощью метода класса:

 NSImage *segment = nil;

  (NSImage *)imageSegment
{
    if (segment == nil) segment = [[NSImage alloc] initWithContentsOfFile:@"/user/asd.jpg"];
    return segment;
}
  

Комментарии:

1. Почему у вас может быть статическая NSString — с использованием синтаксиса @»myString», если NSString является object ?

2. @bluesm: Компилятор Objective-C использует некоторые хитрости для создания строки, которая обрабатывается как постоянное значение.

Ответ №3:

Потому что вы просите компилятор инициализировать статическую переменную кодом, который по своей сути является динамическим.

Ответ №4:

Причина в том, что вы определяете свою imageSegment вне функции в вашем исходном коде (статическая переменная).

В таких случаях инициализация не может включать выполнение кода, такого как вызов функции или выделение класса. Инициализатором должна быть константа, значение которой известно во время компиляции.

Затем вы можете инициализировать свою статическую переменную внутри вашего init метода (если вы отложите ее объявление до инициализации).

Комментарии:

1. Проблема заключается в статическом классе хранилища. Проблема все равно возникла бы, даже если бы она была внутри функции (и объявлена статической).

2. @noloader: Я никогда не утверждал обратного… 🙂 только конкретным случаем статического хранилища в случае операционной системы была глобальная переменная… (в скобках концепция — все зависит от того, кто читает, лучше ли начинать с концепции или с конкретного случая).

Ответ №5:

Вы, конечно, можете #define макрос, как показано ниже. Компилятор заменит «IMAGE_SEGMENT» на его значение перед компиляцией. Хотя вы достигнете определения глобального поиска для вашего массива, это не то же самое, что глобальная переменная. При развертывании макроса он работает точно так же, как встроенный код, и поэтому каждый раз создается новое изображение. Итак, если вы будете осторожны при использовании макроса, то вы бы эффективно добились создания глобальной переменной.

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

Затем используйте его там, где вам это нужно, как показано ниже. Каждый раз, когда выполняется приведенный ниже код, создается новый объект с новым указателем памяти.

 imageSegment = IMAGE_SEGMENT
  

Ответ №6:

Вы можете использовать статический одноэлементный подход с dispatch_once :

 #define STATIC_VAR(type, code) ^type() { 
    static type object; 
    static dispatch_once_t onceToken; 
    dispatch_once(amp;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();
}
  

Он потокобезопасен, лениво работает с любым типом и создает только один экземпляр.

Ответ №7:

Я получил эту ошибку во время практики языка C, мой код, который я пытался запустить, был таким

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

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

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

и я понял, читая ответы, что в C у меня должна быть функция main, которую я забыл использовать, поэтому поместите код person в функцию main, таким образом, удалив ошибку следующим образом

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

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

int main()
{

    person *p = (person *)malloc(sizeof(person));
    return 0;
}