为什么我的 YES BOOL 被 negator 变成了 true bool?

Why is my YES BOOL turned into a true bool by negator?

我有以下代码:

    // ...

    [self performSelectorInBackground:@selector(mySel:) withObject:@YES];
}

// ...

- (void) mySel:(BOOL)suppressUserAlerts
{
    NSError*error;
    [obj doActionWithError:&error];
    if (!suppressUserAlerts && error) // line with breakpoint
    {
        [self alertOfError:error];
    }

    // ...

现在,即使我传入了 @YESalertOfError: 也总是被调用。所以,很自然地,我调试了一下,加了一些watched表达式和断点,结果发现了这个废话:

所以,我很困惑,我问:为什么 suppressUserAlerts YES!suppressUserAlerts true 而且,更多重要的是,如何获得我想要的值?

BOOL 不是 Objective-C 中的对象。修复此错误的一种方法是通过 NSNumber(对象)传递 BOOL 值(只是 0 或 1)。

[self performSelectorInBackground:@selector(mySel:) withObject:[NSNumber numberWithBool:YES];

- (void)mySel:(id)suppressUserAlerts {
    BOOL suppressUserAlertsBool = suppressUserAlerts.boolValue;
    // ...
}

编辑: 请注意,您仍然可以只传递 @YES 而不是 [NSNumber numberWithBool:YES],因为 Cocoa 将为您处理转换。我只是使用 numberWithBool 来更明确。

@timgcarlson 的修正是正确的。

不过,要回答你的问题:

您实际上是在 @YES 传递一个对象。如果您现在有语句

if (!supressUserAlerts) {

你实际上是在测试对象是否存在(等同于supresseUserAlerts == nil)。 BOOL 声明更像是一个只影响调试器的强制转换。

所以你的陈述永远是正确的,因为 @YES 创建了一个不等于 nil 的对象。

编辑

以正确的方式修复您的错误:

    // ...
    [self performSelectorInBackground:@selector(mySel:) withObject:@YES];
}

- (void)mySel:(NSNumber *)suppressUserAlerts {
    NSError *error;
    [obj doActionWithError:&error];

    if (![suppressUserAlerts boolValue] && error) {
        [self alertOfError:error];
    }

    // ...

编辑二

我认为您在调试器中看到的只是它输出标量布尔值(由 ! 前缀创建)与对象布尔值的方式。

这一行:

[self performSelectorInBackground:@selector(mySel:) withObject:@YES];

正在传递一个 NSNumber 实例。这里有必要使用对象而不是标量 BOOL 值,因为 -performSelectorInBackground:withObject: 只能处理对象类型的参数(如 "withObject:" 建议的那样)。

但是,您的方法 -mySel: 使用 BOOL 参数。框架中的任何内容都不会自动将 NSNumber 拆箱以将其转换为 BOOL。相反,您的方法正在接收一个对象指针值,但将其解释为 BOOL.

现在,BOOL 是一个 unsigned char,或者在某些架构上,是一个 bool。因此,"if" 语句只检查低字节或低位。指向 NSNumber 的指针很可能具有零低字节或零位。请注意,NSNumber 对象的值是什么并不重要。仅检查其地址。

调试器可能很困惑。它可能相当于 "po <full 64-bit register value holding the object pointer>"。因此,即使您的程序只检查低位字节或位,调试器也会检查完整的 64 位值并将其视为对象指针。因此,调试器会误导您应该发生什么。

正如其他人所建议的那样,您可以更改 -mySel: 方法以采用 NSNumber* 而不是 BOOL。您将不得不使用 -boolValue 方法来检查该对象的值。

但是,您也可以通过使用 Grand Central Dispatch (GCD) 而不是 -performSelectorInBackground:withObject: 来避免此类问题。您的代码可以是:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self mySel:YES];
});

不需要将 YES 值装箱到对象中,因此在方法中被错误解释也没有问题。而且,如果您确实遇到了类型不匹配,编译器可以识别它并警告您,这是 -performSelector... 方法无法做到的。

或者,如果你真的不需要 -mySel: 方法,除非你想通过选择器引用它,你可以直接将它的代码内联到块中。