在Objective-C中发送一条消息给nil

作为正在阅读Apple的Objective-C 2.0文档的Java开发人员,我想知道“ 向nil发送消息 ”是什么意思 – 更不用说它实际上有用了。 从文档摘录:

cocoa中有几种模式利用了这个事实。 从消息返回到零的值也可能是有效的:

  • 如果方法返回一个对象,任何指针types,小于或等于sizeof(void *),float,double,long double或long long的任何整数标量,则发送到nil的消息返回0 。
  • 如果方法返回一个结构,如Mac OS X ABI函数调用指南中定义的那样,返回寄存器中,那么发送给nil的消息将返回0.0,用于数据结构中的每个字段。 其他结构数据types不会被填充零。
  • 如果该方法返回的值不是上述的值types,则发送给nil的消息的返回值是未定义的。

Java让我的大脑无法对上面的解释进行修改吗? 或者是有什么我失踪,这将如玻璃一样清晰?

我在Objective-C中得到了消息/接收器的概念,我只是对接收器感到困惑。

那么,我认为这可以用一个非常人为的例子来描述。 假设你有一个Java方法,它打印出ArrayList中的所有元素:

 void foo(ArrayList list) { for(int i = 0; i < list.size(); ++i){ System.out.println(list.get(i).toString()); } } 

现在,如果你调用这样的方法:someObject.foo(NULL); 当它尝试访问列表时,你可能会得到一个NullPointerException,在这种情况下,调用list.size(); 现在,你可能永远不会像这样的NULL值调用someObject.foo(NULL)。 然而,你可能已经从一个返回NULL的方法得到你的ArrayList,如果运行时产生一些像someObject.foo(otherObject.getArrayList())的ArrayList的错误;

当然,如果你这样做,你也会遇到问题:

 ArrayList list = NULL; list.size(); 

现在,在Objective-C中,我们有等价的方法:

 - (void)foo:(NSArray*)anArray { int i; for(i = 0; i < [anArray count]; ++i){ NSLog(@"%@", [[anArray objectAtIndex:i] stringValue]; } } 

现在,如果我们有以下代码:

 [someObject foo:nil]; 

我们有相同的情况,Java将产生一个NullPointerException。 首先在[anArray count]处访问nil对象。然而,Objective-C不是抛出一个NullPointerException,而是根据上面的规则简单地返回0,所以循环将不会运行。 但是,如果我们设置循环运行一个设定的次数,那么我们首先发送一个消息给aArray [anArray objectAtIndex:i]; 这也会返回0,但是因为objectAtIndex:返回一个指针,并且指向0的指针是nil / NULL,所以NSLog每次都会在循环中通过nil。 (尽pipeNSLog是一个函数,而不是一个方法,如果传递一个零的NSString,它会打印出(null)。

在某些情况下,有一个NullPointerException会更好,因为您可以立即告诉程序出了什么问题,除非您发现exception,否则程序将崩溃。 (在C中,试图以这种方式解引用NULL导致程序崩溃。)在Objective-C中,它只是导致可能不正确的运行时行为。 但是,如果你有一个方法不会中断,如果它返回0 / nil / NULL /一个零结构,那么你不必检查,以确保对象或参数为零。

一个消息nil不做任何事情,并返回nilNilNULL00.0

所有其他的post是正确的,但也许这是这个概念是重要的这里。

在Objective-C方法调用中,任何可以接受select器的对象引用都是该select器的有效目标。

这节省了很多“是typesX的目标对象?” 代码 – 只要接收对象实现了select器,它就完全没有区别它是什么类! nil是一个接受任何select器的NSObject – 它只是不做任何事情。 这消除了很多“检查零,不要发送信息,如果真”代码。 (“如果它接受它,它实现它”的概念也允许你创build协议 ,它们就像Java接口一样:声明如果一个类实现了规定的方法,那么它就符合协议。

这样做的原因是消除了猴子代码,除了保持编译器的快乐之外什么都不做。 是的,你会得到一个方法调用的开销,但是你节省了程序员的时间 ,这是比CPU时间更昂贵的资源。 另外,您将从应用程序中消除更多的代码和更多的条件复杂性。

澄清downvoters:你可能认为这不是一个好的方法,但这是如何实现语言,这是Objective-C推荐的编程习惯(见斯坦福iPhone编程讲座)。

这意味着当nil指针调用objc_msgSend时,运行时不会产生错误; 而是返回一些(通常是有用的)值。 可能有副作用的信息什么也不做。

这很有用,因为大多数默认值比错误更合适。 例如:

 [someNullNSArrayReference count] => 0 

即,零似乎是空arrays。 隐藏一个零的NSView引用什么都不做。 方便,呃?

在文档引用中,有两个单独的概念 – 如果文档更加清晰,也许会更好:

cocoa中有几种模式利用了这个事实。

从消息返回到零的值也可能是有效的:

前者在这里可能更为相关:通常能够将消息发送到nil使得代码更直接 – 您无需在任何地方检查空值。 规范的例子可能是访问方法:

 - (void)setValue:(MyClass *)newValue { if (value != newValue) { [value release]; value = [newValue retain]; } } 

如果发送消息到nil是无效的,这个方法会更复杂 – 你必须有两个额外的检查,以确保valuenewValue不是nil发送消息之前。

后一点(从消息返回到nil值通常也是有效的)虽然给前者增加了乘数效应。 例如:

 if ([myArray count] > 0) { // do something... } 

此代码再次不需要检查nil值,并自然stream动…

所有这一切说,能够发送消息到nil的额外的灵活性是有一定的代价。 有可能你会在某个阶段编写代码失败,因为你没有考虑到一个值可能nil的可能性。

从Greg Parker的网站 :

如果运行LLVM Compiler 3.0(Xcode 4.2)或更高版本

消息到nil返回types| 返回
整数最多64位|  0
浮点长达double double |  0.0
指针| 零
结构|  {0}
任何_Complextypes|  {0,0}

这意味着通常不必为了安全而检查无处不在的对象,特别是:

 [someVariable release]; 

或者如上所述,当你有一个零值时,各种计数和长度方法都会返回0,所以你不必为整个零增加额外的检查:

 if ( [myString length] > 0 ) 

或这个:

 return [myArray count]; // say for number of rows in a table 

不要去想“接收器是零”。 我同意,这很奇怪。 如果你发送消息到零,没有接收器。 你只是发一个消息而已。

如何处理这是Java和Objective-C之间的哲学区别:在Java中,这是一个错误; 在Objective-C中,这是一个没有任何操作。

发送给nil的返回值大于sizeof(void *)的ObjC消息在PowerPC处理器上产生未定义的值。 除此之外,这些消息还导致未定义的值在英特尔处理器上的大小超过8字节的结构字段中返回。 文森特·盖博在他的博客文章中很好地描述了这一点

我不认为任何其他的答案都清楚地提到了这一点:如果你习惯了Java,你应该记住,虽然Mac OS X上的Objective-C具有exception处理支持,但它是一个可选的语言特性,可以是用编译器标志打开/closures。 我的猜测是,这个“发送消息到nil是安全的”devise早于在语言中包含exception处理支持,并且考虑到类似的目标:方法可以返回nil来指示错误,并且由于发送消息到nil通常依次返回nil ,这就允许错误指示在你的代码中传播,所以你不必在每一条消息中检查它。 你只需要在重要的地方检查它。 我个人认为exception传播和处理是实现这个目标的更好的方法,但不是每个人都可能同意这一点。 (另一方面,例如,我不喜欢Java的要求,你必须声明一个方法可能抛出什么exception,这常常会迫使你在你的代码中语法上传播exception声明;但这是另外一个讨论。)

我已经发布了一个相似的,但更长的回答相关的问题“是否断言每个对象创build成功必须在Objective C? 如果你想要更多的细节。

对于原始值,C代表0,对于指针(在指针上下文中相当于0),代表NULL。

Objective-C通过添加nilbuild立在C的表示上。 零是一个对象指针什么也没有。 尽pipe在语义上与NULL不同,但它们在技术上相当于另一个。

新分配的NSObjects将其内容设置为0,这意味着所有指向其他对象的指针都以nil开头,所以没必要在init方法中设置self。(association)= nil。

nil的最显着的行为是,它可以有消息发送给它。

在其他语言中,如C ++(或Java),这会使你的程序崩溃,但是在Objective-C中,在nil上调用一个方法会返回一个零值。 这极大地简化了expression式,因为它在执行任何操作之前不需要检查零。

 // For example, this expression... if (name != nil && [name isEqualToString:@"Steve"]) { ... } // ...can be simplified to: if ([name isEqualToString:@"Steve"]) { ... } 

意识到Objective-C中nil的工作方式如何让这种便利性成为一项function,而不是您应用程序中潜伏的错误。 请确保防止不需要的nil值的情况,或者通过提前检查并返回以无提示方式失败,或者添加NSParameterAssert来引发exception。

来源: http : //nshipster.com/nil/ https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html (发送消息为零)。