[Objective-C++]为什么64位允许给BOOL类型变量赋值对象,32位不允许
[Objective-C++]Why assigning object to BOOL type variable is allowed in 64bit but not in 32bit
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'
我知道这个分配是错误的,但为什么它在第一种情况下允许?
错误表明您正在将对象分配给 BOOL
类型的变量。要解决它,您需要使用 boolValue
.
将从 objectForKey: 返回的对象转换为 BOOL
变量
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
对于 64 位 iOS,BOOL
类型使用 C99 _Bool
类型(有时也可用作 bool
)。该类型被定义为只有两个值,0 或 1。将任何其他值分配给该类型的变量会导致它取值 1。(也就是说,所有非零值都变为 1。)
因此,赋值不会截断按位值,可能会将非零值转换为零。因此,它在某种程度上是 "safe" 并且没有理由发出警告。 (对于不理解对象指针及其值之间区别的天真的开发人员来说,这可能仍然令人惊讶,正如其他答案已经解决的那样,但这是另一回事。)
有几件事解释了为什么会这样,其中很多是历史上的东西,只是因为它曾经是那样。
bool
是比较新的类型。如果你看旧的 C 代码,你会经常看到他们只是使用 int
,或者喜欢 Objective-C 编译器并声明他们自己的 Bool
、BOOL
或 Boolean
类型.
回到最初定义 Objective-C 时,他们使用尽可能小的类型 (signed char
) 作为 BOOL
类型。
C if
语句始终允许使用指针作为其条件,以测试指针不是 NULL
(或 nil
在 ObjC 的情况下) .
当 bool
被添加到 C 标准时,它基本上被定义为 "the type that if
takes" 因此必须具有所有相同的行为,包括允许您提供一个指针在需要 bool 的地方,如果 nil
,则将其计算为 false
,否则计算为 true
。
Apple 无法在旧机器上将 BOOL
更改为 bool
,因为它会更改 @encode()
为 [= 返回的符号13=] 并使旧程序无法链接到与新库相同的进程中(反之亦然)。
当 64 位 iOS 到来时,许多数据类型已经改变了大小,因此 Apple 可以安全地将 BOOL
更改为 bool
,因为那里没有旧程序需要能够 运行 在与新库相同的内存 space 中。一切都是新的。
因此,在将 BOOL
新定义为 bool
的平台上,您会遇到 C 的向后兼容功能,让您检查 NULL
指针。在旧平台上,BOOL
被定义为 signed char
,它显然不适合指针,并且没有指针的特殊调味料,因此会导致警告。
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'
我知道这个分配是错误的,但为什么它在第一种情况下允许?
错误表明您正在将对象分配给 BOOL
类型的变量。要解决它,您需要使用 boolValue
.
BOOL
变量
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
对于 64 位 iOS,BOOL
类型使用 C99 _Bool
类型(有时也可用作 bool
)。该类型被定义为只有两个值,0 或 1。将任何其他值分配给该类型的变量会导致它取值 1。(也就是说,所有非零值都变为 1。)
因此,赋值不会截断按位值,可能会将非零值转换为零。因此,它在某种程度上是 "safe" 并且没有理由发出警告。 (对于不理解对象指针及其值之间区别的天真的开发人员来说,这可能仍然令人惊讶,正如其他答案已经解决的那样,但这是另一回事。)
有几件事解释了为什么会这样,其中很多是历史上的东西,只是因为它曾经是那样。
bool
是比较新的类型。如果你看旧的 C 代码,你会经常看到他们只是使用int
,或者喜欢 Objective-C 编译器并声明他们自己的Bool
、BOOL
或Boolean
类型.回到最初定义 Objective-C 时,他们使用尽可能小的类型 (
signed char
) 作为BOOL
类型。C
if
语句始终允许使用指针作为其条件,以测试指针不是NULL
(或nil
在 ObjC 的情况下) .当
bool
被添加到 C 标准时,它基本上被定义为 "the type thatif
takes" 因此必须具有所有相同的行为,包括允许您提供一个指针在需要 bool 的地方,如果nil
,则将其计算为false
,否则计算为true
。Apple 无法在旧机器上将
BOOL
更改为bool
,因为它会更改@encode()
为 [= 返回的符号13=] 并使旧程序无法链接到与新库相同的进程中(反之亦然)。当 64 位 iOS 到来时,许多数据类型已经改变了大小,因此 Apple 可以安全地将
BOOL
更改为bool
,因为那里没有旧程序需要能够 运行 在与新库相同的内存 space 中。一切都是新的。
因此,在将 BOOL
新定义为 bool
的平台上,您会遇到 C 的向后兼容功能,让您检查 NULL
指针。在旧平台上,BOOL
被定义为 signed char
,它显然不适合指针,并且没有指针的特殊调味料,因此会导致警告。