为什么NSNumber文字不能用于静态声明

gia*_*olo 6 static objective-c nsnumber ios ios6

我宣布:

static NSString *a = @"a";
Run Code Online (Sandbox Code Playgroud)

这是iOS6中的正确声明(使用编译器版本应该更正确,但目前我还不知道).我认为数字文字也是:

static NSNumber *b=@1;
Run Code Online (Sandbox Code Playgroud)

可能是一个有效的声明.编译器告诉我initializer element is not a compile time constant.这让我感到惊讶.既然NSNumber是不可变的NSString,因为我在字符串的情况下使用文字,我认为它也可以是有效的.

有没有人对这种差异有合理的解释?

iDe*_*Dev 18

第一行是编译时常量,因为你正在分配@"a"而不是类似的东西static NSString *a = [NSString stringWithFormat:@"a"];(这将抛出相同的错误)

但对于一个NSNumber,static NSNumber *b = @1;实际上相当于static NSNumber *b = [NSNumber numberWithInt:1];.有关更多详细信息,请查看Objective-C Literals.

请注意,在上述情况下,右侧不是编译时常量.它是一个必须在运行时计算的表达式.在C和Objective-C中,必须使用编译时常量初始化静态变量.

如果你想拥有NSNumber,const你可以检查这里提到的方法目标C - 如何使用外部变量?.

请参阅Mike Ash的Objective C文字,

值得注意的是,新的文字语法都不能作为编译时常量.

和,

NSString文字也是编译时常量,因为编译器和库之间存在紧密耦合.有一个特殊的NSString子类,名为NSConstantString,具有固定的ivar布局:

这种紧密耦合具有诸如生成合法的全局变量初始化器之类的优点,并且不需要运行额外的代码来在运行时构建对象.但是,也存在很大的缺点.NSConstantString布局永远设置.必须使用完全相同的数据布局来维护该类,因为该数据布局已融入数千个第三方应用程序中.如果Apple更改了布局,那些第三方应用程序将会中断,因为它们包含具有旧布局的NSConstantString对象.

如果NSArray文字是编译时常量,则需要一个类似的NSConstantArray类,它具有编译器可以生成的固定布局,并且必须与其他NSArray实现分开维护.此类代码无法在没有此NSConstantArray类的旧操作系统上运行.新文字可以产生的其他类存在同样的问题.

在NSNumber文字的情况下,这尤其有趣.Lion引入了标记指针,允许将NSNumber的内容直接嵌入指针中,从而无需单独的动态分配对象.如果编译器发出标记指针,它们的格式永远不会改变,并且与旧操作系统版本的兼容性将会丢失.如果编译器发出了常量NSNumber对象,那么NSNumber文字将与其他NSNumber显着不同,可能会有很大的性能损失.

相反,编译器只是调用框架,完全像手动完成一样构造对象.这会导致一点运行时命中,但没有比没有新语法自己构建它们更糟糕的情况,并且使设计更加简洁.