[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" 并且没有理由发出警告。 (对于不理解对象指针及其值之间区别的天真的开发人员来说,这可能仍然令人惊讶,正如其他答案已经解决的那样,但这是另一回事。)

有几件事解释了为什么会这样,其中很多是历史上的东西,只是因为它曾经是那样。

  1. bool是比较新的类型。如果你看旧的 C 代码,你会经常看到他们只是使用 int,或者喜欢 Objective-C 编译器并声明他们自己的 BoolBOOLBoolean类型.

  2. 回到最初定义 Objective-C 时,他们使用尽可能小的类型 (signed char) 作为 BOOL 类型。

  3. C if 语句始终允许使用指针作为其条件,以测试指针不是 NULL(或 nil 在 ObjC 的情况下) .

  4. bool 被添加到 C 标准时,它基本上被定义为 "the type that if takes" 因此必须具有所有相同的行为,包括允许您提供一个指针在需要 bool 的地方,如果 nil,则将其计算为 false,否则计算为 true

  5. Apple 无法在旧机器上将 BOOL 更改为 bool,因为它会更改 @encode() 为 [= 返回的符号13=] 并使旧程序无法链接到与新库相同的进程中(反之亦然)。

  6. 当 64 位 iOS 到来时,许多数据类型已经改变了大小,因此 Apple 可以安全地将 BOOL 更改为 bool,因为那里没有旧程序需要能够 运行 在与新库相同的内存 space 中。一切都是新的。

因此,在将 BOOL 新定义为 bool 的平台上,您会遇到 C 的向后兼容功能,让您检查 NULL 指针。在旧平台上,BOOL 被定义为 signed char,它显然不适合指针,并且没有指针的特殊调味料,因此会导致警告。