为什么我的YES BOOL被否定者变成了真正的bool?

时间:2015-07-22 22:33:06

标签: objective-c boolean

我有以下代码:

    // ...

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

// ...

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

    // ...

现在,即使我传入@YES,也始终会调用alertOfError:。所以,当然,我调试了它并添加了一些观察到的表达式和断点,我发现这是胡说八道:

A screenshot of two expressions: <code>!suppressUserAlerts = (bool) true</code> and <code>suppressUserAlerts = (BOOL) YES</code>

所以,令人困惑,我问:为什么suppressUserAlerts YES!suppressUserAlerts true更重要的是,我如何获得我想要的价值?

3 个答案:

答案 0 :(得分:2)

@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];
    }

    // ...

编辑II

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

答案 1 :(得分:1)

这一行:

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

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

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

现在,BOOLunsigned char,或者在某些体系结构上是bool。因此,只有低位字节或低位由“if”语句检查。指向NSNumber的指针很可能具有零低字节或位。请注意,NSNumber对象的值是什么并不重要。只检查其地址。

调试器可能很混乱。它可能相当于“保持对象指针&gt;的po&lt;完整64位寄存器值”。因此,即使您的程序只检查低字节或位,调试器也会检查完整的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:方法,除非您想通过选择器引用它,您可以直接将其代码内联到块中。

答案 2 :(得分:0)

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

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

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

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