Помилка компілятора: “Елемент ініціалізатора не є константою часу компіляції”


76

При компіляції цього коду я отримую помилку "Елемент ініціалізатора не є константою часу компіляції". Хтось може пояснити, чому?

#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

Відповіді:


105

Коли ви визначаєте змінну поза сферою дії функції, значення цієї змінної фактично записується у ваш виконуваний файл. Це означає, що ви можете використовувати лише постійне значення. Оскільки ви не знаєте всього про середовище виконання під час компіляції (які класи доступні, яка їх структура тощо), ви не можете створювати об'єктивні 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;
}

4
Інший варіант - використовувати функцію з __attribute__ ((constructor)).

12
Ще один варіант - переключити тип вихідного файлу з Objective-C на Objective-C ++ (або перейменувати його з .m на .mm, що має той самий ефект). У C ++ такі ініціалізатори не повинні мати значення константи часу компіляції, і оригінальний код працював би чудово.
імре

1
Чи є спосіб задекларувати constтакий спосіб? Тобто змінну, яку можна встановити лише один раз і більше ніколи?
devios1

Що робити, якщо я хочу включити константу з декількох файлів. Чи можна записати функцію ініціалізації кілька разів, тобто. чи може він складатися з кількох частин?
Володимир Деспотович

@VladimirDespotovic Ні, це не можна розділити. Ви можете мати +initializeметод для різних класів, або він може викликати функції з інших файлів, але ви повинні бути дуже обережними з подібними речами. Насправді найкраще уникати цього, якщо це можливо.
ughoavgfhw

23

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

Якщо ви хочете мати глобальну змінну, вам слід її ініціалізувати, nilа потім повернути за допомогою методу класу:

NSImage *segment = nil;

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

Чому у вас може бути статичний NSString - за допомогою синтаксису @ "myString", якщо NSString є об'єктом?
Paul Brewczynski

4
@bluesm: Компілятор Objective-C використовує деякі хитрощі для створення рядка, який розглядається як постійне значення.
mipadi

11

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


6

Причина в тому, що ви визначаєте свою imageSegmentзовнішню частину функції у своєму вихідному коді (статична змінна).

У таких випадках ініціалізація не може включати виконання коду, наприклад, виклик функції або виділення класу. Ініціалізатор повинен бути константою, значення якої відомо під час компіляції.

Потім ви можете ініціалізувати вашу статичну змінну всередині вашого initметоду (якщо ви відкладете її оголошення на init).


1
Проблемою є статичний клас зберігання. Проблема все одно виникає, навіть якщо вона знаходиться всередині функції (і оголошена статичною).
jww

@noloader: Я ніколи не стверджував протилежного ... :-) лише, конкретний випадок статичного зберігання у випадку OP був глобальною змінною ... (у дужках, концепція - все залежить від того, хто читач, чи краще починати з концепції або конкретного випадку).
sergio

4

Ви, звичайно, можете #define макрос, як показано нижче. Компілятор замінить "IMAGE_SEGMENT" своїм значенням перед компіляцією. Хоча ви досягнете визначення загального пошуку для вашого масиву, це не те саме, що глобальна змінна. Коли макрос розгорнутий, він працює так само, як і вбудований код, тому кожен раз створюється нове зображення. Отже, якщо ви будете обережні, де ви використовуєте макрос, тоді ви б ефективно досягли створення глобальної змінної.

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

Потім використовуйте його там, де вам це потрібно, як показано нижче. Кожного разу, коли виконується наведений нижче код, створюється новий об’єкт з новим покажчиком пам’яті.

imageSegment = IMAGE_SEGMENT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.