ViewController.mm中的以下代码将在64位环境中成功编译(没有任何警告或错误)。
- (void)viewDidLoad {
[super viewDidLoad];
BOOL var = [[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];
NSLog(@"%d", var);
}
但是当我将运行目标更改为32位设备(例如,iPhone 5)时,它会显示如下错误:
Cannot initialize a variable of type 'BOOL' (aka 'signed char') with an rvalue of type 'id _Nullable'
我知道这个任务是错误的,但为什么它在第一种情况下允许?
对于64位iOS,BOOL
类型使用C99 _Bool
类型(有时也可用作bool
)。该类型被定义为仅具有两个值0或1.将任何其他值分配给该类型的变量使得它取值1.(即,所有非零值变为1。)
因此,赋值不会截断按位值,可能会将非零值转换为零。因此,它在某个方面是“安全的”,并且没有理由发出警告。 (对于一个天真的开发人员来说,如果没有理解对象指针与其值之间的差异,可能仍然会感到惊讶,正如其他答案所解决的那样,但这是另一回事。)
错误表示您正在将对象分配给BOOL
类型的变量。要解决它,您需要使用objectForKey:将从BOOL
返回的对象转换为boolValue
变量。
BOOL var = [[[NSUserDefaults standardUserDefaults] objectForKey:@"foo"] boolValue];
你的代码:
BOOL var = [[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];
将对象的地址分配给某个大小的int ...
这几乎总是不是你想要的。你可能想要:
BOOL var = [[[NSUserDefaults standardUserDefaults] objectForKey:@"foo"]boolValue];
如果您确实希望根据该对象的存在进行分配,您可以执行以下操作:
BOOL var = !![[NSUserDefaults standardUserDefaults] objectForKey:@"foo"];
原因是,如果地址的底部8位全部为0,则它可能是有效对象,但是将是假值,这将是截断赋值的结果。
int main(int argc, const char * argv[]) {
BOOL b = 2;
if (b == YES)
{
NSLog(@"I guess you are right");
}else{
NSLog(@"nope");
}
打印:nope
有几个方面可以解释为什么这个有效,其中很多都是历史性的东西,因为它曾经是那样。
bool
是一种相对较新的类型。如果你看一下较旧的C代码,你会经常看到他们只是使用int
,或者像Objective-C编译器一样,并声明他们自己的Bool
,BOOL
或Boolean
类型。signed char
)作为他们的BOOL
类型。if
语句总是允许使用指针作为其条件,以测试指针不是NULL
(或ObjC的情况下的nil
)。bool
被添加到C标准时,它基本上被定义为“if
所采用的类型”,因此必须具有所有相同的行为,包括允许您提供指向bool的指针,并将其评估为false
如果nil
,true
否则。BOOL
更改为bool
,因为它会改变@encode()
为BOOL
返回的符号,并使旧程序无法链接到与新库相同的过程(反之亦然)。BOOL
更改为bool
,因为没有旧程序需要能够在与新库相同的内存空间中运行。一切都是新的。因此,在BOOL
的新定义为bool
的平台上,您遇到了C的向后兼容性功能,让您检查NULL
指针。在较旧的平台上,BOOL
被定义为signed char
,显然不适合指针,并且没有指针的特殊酱,因此导致警告。