我今天在源文件中发现了一条评论:
// - no longer compare BOOL against YES (dangerous!)
在Objective-C中将BOOL
与YES
进行比较真的那么危险吗?那为什么呢?
YES
的值可以在运行时更改吗?也许NO
总是0
但YES
可以是1
,2
或3
- 取决于运行时,编译器,链接框架?< / p>
答案 0 :(得分:17)
问题是BOOL不是本机类型,而是typedef:
typedef signed char BOOL;
#define YES (BOOL)1
#define NO (BOOL)0
作为字符,其值不受TRUE
和FALSE
的约束。另一个值会发生什么?
BOOL b = 42;
if (b)
{
// true
}
if (b != YES)
{
// also true
}
答案 1 :(得分:11)
在任何基于C的语言中,都不应该将布尔值与任何进行比较。正确的方法是使用:
if (b)
或:
if (!b)
这使您的代码更具可读性(特别是如果您使用智能命名的变量和函数,如isPrime(n)
或childThreadHasFinished
)并且安全。原因如下:
if (b == TRUE)
不太安全,实际上有大量的b
值将评估为true,而TRUE
只是其中之一。
请考虑以下事项:
#define FALSE 0
#define TRUE 1
int flag = 7;
if (flag) printf ("number 1\n");
if (flag == TRUE) printf ("number 2\n");
如果按预期的方式工作,你应该这两行打印出来但你只得到第一行。这是因为如果正确处理7 实际上是真的(0是假,其他一切都是真的)但是对等式的显式测试评估为假。
更新
回应你的评论,你认为除了编码器的愚蠢之外还有更多的东西:是的,有(但我仍然不会将编码器的愚蠢视为一个足够好的理由 - 防御性编程总是一个好主意)
我还提到了可读性,这在我的代码中所需功能列表中相当高。
条件应该是对象或标志之间的比较(包括布尔返回值):
if (a == b) ...
if (c > d) ...
if (strcmp (e, "Urk") == 0) ...
if (isFinished) ...
if (userPressedEsc (ch)) ...
如果你使用(我认为)令人憎恶的话:
if (isFinished == TRUE) ...
你在哪里停止:
if (isFinished == TRUE) ...
if ((isFinished == TRUE) == TRUE) ...
if (((isFinished == TRUE) == TRUE) == TRUE) ...
等等。
正确这样做的可读性方法是使用适当命名的标志变量。
答案 2 :(得分:2)
所有这一切都是正确的,但可以考虑有效的反驳论据:
- 也许我们想要来检查BOOL实际上是YES还是NO。实际上,在BOOL中存储除0或1之外的任何其他值是非常不正确的。如果它发生了,不是因为代码库中其他地方的bug而更有可能,并且没有明确地检查YES只是屏蔽了这个bug?我认为这比一个以非标准方式使用BOOL的邋program程序员更有可能。所以,如果我在寻找真相时我的BOOL不是,那么我认为我希望我的测试失败。
- 我不一定同意&#34; if(isWhatever)&#34;更具可读性,尤其是在评估长期但可读的函数调用时,
e.g。比较
if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]]) {}
使用:
if (![myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]]) {}
第一个是比较真实,第二个是反对虚假,快速阅读代码很难区分,对吗?
将其与:
进行比较if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]] == YES) {}
和
if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]] == NO) {}
......并且它的可读性更高吗?
同样,我并不是说一种方法是正确的而另一方面是错误的,但有一些对立点。
答案 3 :(得分:0)
当代码使用BOOL
变量时,它应该使用这样的变量作为布尔值。编译器不检查BOOL
变量是否获得不同的值,就像编译器不检查是否初始化传递给具有在一组常量之间取值的方法的变量一样。