【问题标题】:Compiler error: "initializer element is not a compile-time constant"编译器错误:“初始化元素不是编译时常量”
【发布时间】:2011-09-02 20:10:54
【问题描述】:

编译此代码时,我收到错误“初始化程序元素不是编译时常量”。谁能解释一下为什么?

#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

【问题讨论】:

    标签: objective-c cocoa initialization compiler-errors global-variables


    【解决方案1】:

    当您在函数范围之外定义变量时,该变量的值实际上会写入您的可执行文件。这意味着您只能使用常量值。由于您在编译时不了解运行时环境的所有信息(哪些类可用,它们的结构是什么等),所以在运行时之前您无法创建目标 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;
    }
    

    【讨论】:

    • 另一种选择是使用带有__attribute__ ((constructor))的函数。
    • 还有一个选择是将源文件的类型从 Objective-C 切换到 Objective-C++(或将其从 .m 重命名为 .mm,效果相同)。在 C++ 中,此类初始化程序不需要是编译时常量值,原始代码也可以正常工作。
    • 有没有办法以这种方式声明const? IE。一个只能设置一次且不能再设置的变量?
    • 如果我想从多个文件中包含一个常量怎么办。是否可以多次编写初始化函数,即。它可以由几个部分组成吗?
    • @VladimirDespotovic 不,不能拆分。您可以为不同的类使用+initialize 方法,或者它可以从其他文件调用函数,但是您必须非常小心这样的事情。如果可能,最好避免它。
    【解决方案2】:

    必须将全局变量初始化为常量值,例如 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 是对象,使用 @"myString" 语法?
    • @bluesm:Objective-C 编译器使用一些技巧来创建一个被视为常量值的字符串。
    【解决方案3】:

    因为您要求编译器使用本质上是动态的代码来初始化静态变量。

    【讨论】:

      【解决方案4】:

      原因是您在源代码中的函数(静态变量)之外定义了 imageSegment

      在这种情况下,初始化不能包括代码的执行,例如调用函数或分配类。 Initializer 必须是一个常量,其值在编译时是已知的。

      然后您可以在 init 方法中初始化您的静态变量(如果您将其声明推迟到 init)。

      【讨论】:

      • 静态存储类是问题所在。即使它在函数内部(并声明为静态),问题仍然会发生。
      • @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(&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();
        }
        

        它是线程安全的,可以懒惰地处理任何类型并且只创建一个实例。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-18
          • 2012-03-22
          • 1970-01-01
          • 2012-08-31
          • 2011-05-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多