Objective-C中的!与== nil有何区别?

15

如果你有一个类似NSString *someString的对象,那么它与下面哪个有什么不同(如果有):

if (!someString)

vs

的翻译是:vs。
if (someString == nil)

谢谢!

6个回答

23

你使用的第一个语法:

 if (!someString)

利用 C 语言的一种“歧义”,因为最初的 C 标准缺乏适当的布尔类型。因此,任何等于 0 的整数值都被解释为“false”,而任何不为“0”的整数值则被视为“true”。因此,!的含义基于这个约定进行定义,并且当前版本的 C 标准为了兼容性而保留了原始定义。

在您的特定情况下,someString是一个指针,因此它首先被转换为整数,然后!someString被解释为布尔值true,当someString指向位置0x000000时,否则它将计算为“true”。

这在大多数情况下是可以的(我会说总是),但理论上,NULL/nil 在某些编译器下可能与0x000000不同, 因此(在非常理论的情况下)最好使用第二种语法,这更加明确:

 if (someString == nil)

无论如何,这种写法更易读,并且由于someString不是整数(而是指针),在我看来,这通常是更好的实践。

编辑:关于NULL的定义...

C标准是否将NULL定义为0对我来说是一个有趣的话题...

根据C99标准,第7.17节,“公共定义”:

NULL [扩展为]实现定义的空指针常量;

因此,NULL在stddef.h中被定义为实现定义的空指针常量...同一文档在第47页声明:

值为0的整数常量表达式,或将这样的表达式强制转换为void*类型,称为空指针常量。55)如果将空指针常量转换为指针类型,则得到的指针称为空指针,保证与任何对象或函数的指针比较时都不相等。

因此,空指针常量(即(void*)0)可以转换为空指针,这保证了与任何对象或函数的指针比较时都不相等。

因此,我认为这基本上取决于实现是否决定将将空指针常量转换为空指针的结果产生一个指针,将其转换回整数得到0。不清楚将空指针解释为整数是否等于0。

我认为标准确实试图强制将空指针设为0,但也为那些空指针不是0的系统留下了空间。

2
我给了+1,但在C语言中关于布尔值的概念是没有歧义的 - 它根本不存在。 - Perception
3
C标准将NULL定义为0(void *)0。即使用于空指针值的底层位模式不同,这也不会改变C中空指针常量表示为0(void *)0的事实。 - dreamlax
@Perception:它在C99中确实存在。看一下_Bool - JAB
@Perception:谢谢,我改进了措辞... - sergio
@JAB - 是的,它确实存在。真正的讽刺是,_Bool 实际上只是一个无符号整数,能够存储 0 或 1! stdbool.h 主要是为了向后兼容而设计的,所有条件逻辑实际上仍然保持不变,即使在 c99 中也是如此。 - Perception
显示剩余4条评论

9

对于大多数指针,它们是等效的,尽管我认识的大多数程序员更喜欢前者,因为它更加简洁。

对于弱链接符号,前者会解析该符号(如果缺失将导致崩溃),而显式与nilNULL进行比较则不会。


3

C语言中的感叹号、惊叹号前缀操作符!是一个逻辑非。至少它是逻辑非的一种版本。如果你看过典型的逻辑非真值表,你会看到类似这样的东西:

Input       Result
  1            0
  0            1

然而在 C 语言中,逻辑非运算符的作用更像是这样:

Input       Result
non-zero      0
   0          1

当你考虑到Objective-C中的NULL和nil都等于0时,你知道对它们应用逻辑非运算符将会得到1。
现在,考虑“相等性”操作符“==”。它比较两个项目的值,如果它们相等则返回1,否则返回0。如果将其结果映射到真值表中,它看起来就像逻辑非的结果。
在C和Objective-C程序中,条件判断实际上是由int类型决定的,而不是真正的布尔类型。这是因为在C中没有布尔数据类型。所以在C中写下类似这样的代码是完全没问题的:
if(5) printf("hello\n"); // prints hello

而且,另外
if(2029) printf("hello\n"); // also prints hello

在C语言中,任何非零的整数都会被认为是“真”。结合逻辑否定和等式的真值表,你很快就会意识到:

(! someString) and (someString == nil)

在所有意图上看,它们都是相同的!

那么,下一个合理的问题是,为什么更喜欢一种形式而不是另一种形式?从纯C的观点来看,这主要是风格问题,但大多数(好的)开发者会选择等式测试,原因如下:

  1. 它更接近您在代码中尝试表达的内容。您正在尝试检查someString变量是否为空。
  2. 它更具可移植性。像Java这样的语言有真正的布尔类型。您不能对其变量或其NULL定义使用bang符号表示。在需要时使用相等性使将C轻松移植到这些语言变得更加容易。
  3. 苹果可能会更改nil的定义。好吧,他们不会这样做!但保持谨慎从未有过错!

在C语言中,任何非零整数都会被评估为“true”。您能否更具体地解释逻辑表达式如何评估为(int)0或(int)1?例如,[if(pointer)...],指针首先转换为int [(int)pointer],然后[if((int)pointer != 0)]它将评估为(int)1。指针是64位的,而int是32位的,当它被强制转换为int时,高字节是否被截断(如果(0xffffffff00000000)=> if((int)0x00000000))?我很高兴看到我可能错在哪里。 - MANIAK_dobrii

2

在您的情况下,它意味着相同的事情。任何不指向 nil 的指针都将返回 YES(true)。

通常感叹号运算符否定一个 BOOL 值。


2
如果你的意思是测试条件“foo为nil”,那么应该这样说:foo == nil
如果你想测试布尔值的假,!foo可以,但我个人认为有时候很容易忽略感叹号,所以我更喜欢写成foo == NO
编写好代码不仅要让编译器清楚地知道你的意图,还要让接下来的程序员(可能是未来的你)也能够轻松理解。在两种情况下,你越明确自己的意图,就越能编写出好的代码。
总之,在我能想到的所有情况下,!==nil具有相同的效果。

0

! 是一个否定运算符。如果您的对象未被分配,那么您将从真值表中得到与 == nil 操作相同的结果。

但是,! 通常更多用于布尔运算。

if(!isFalse) {
     //if isFalse == NO, then this operation evaluates to YES (true)
     [self doStuff];
}

当你在对象上使用!,例如!something,它只是检查指针是否指向nil,如果不是,则返回true,并且if语句将被执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接