为什么我的 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];
}
// ...
现在,即使我传入了 @YES
,alertOfError:
也总是被调用。所以,很自然地,我调试了一下,加了一些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:
方法,除非你想通过选择器引用它,你可以直接将它的代码内联到块中。
我有以下代码:
// ...
[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:
也总是被调用。所以,很自然地,我调试了一下,加了一些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:
方法,除非你想通过选择器引用它,你可以直接将它的代码内联到块中。