编译器错误:“初始化器元素不是编译时常量”


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

Answers:


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 ++中,此类初始化程序不需要是编译时常量值,并且原始代码可以正常工作。
imre 2014年

1
有没有办法以const这种方式声明a ?即只能设置一次且不能再次设置的变量?
devios1 2015年

如果我想包含多个文件中的常量怎么办。是否可以多次编写初始化函数,即 它可以由几个部分组成吗?
弗拉基米尔·德斯波托维奇

@VladimirDespotovic不,它不能被分割。您可以+initialize为不同的类提供一个方法,也可以从其他文件中调用函数,但是您必须非常小心此类事情。如果可能的话,最好避免这种情况。
ughoavgfhw

23

全局变量必须初始化为常数值,例如40.0@"constant string"nil。对象构造函数(例如)init不会返回常量值。

如果要使用全局变量,则应将其初始化为nil,然后使用类方法将其返回:

NSImage *segment = nil;

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

为什么您可以拥有静态NSString-如果NSString是object,则使用@“ myString”语法?
Paul Brewczynski 2013年

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.