Sending a message to nil in Objective-C
作为一个正在阅读Apple的Objto-C 2文档的Java开发人员:我想知道"向NIL发送消息"意味着什么——更不用说它实际上是如何有用的了。从文档中摘录:
There are several patterns in Cocoa
that take advantage of this fact. The
value returned from a message to nil
may also be valid:
- If the method returns an object, any pointer type, any integer scalar
of size less than or equal to
sizeof(void*), a float, a double, a
long double, or a long long, then a
message sent to nil returns 0.- If the method returns a struct, as defined by the Mac OS X ABI Function
Call Guide to be returned in
registers, then a message sent to nil
returns 0.0 for every field in the
data structure. Other struct data
types will not be filled with zeros.- If the method returns anything other than the aforementioned value
types the return value of a message
sent to nil is undefined.
Java使我的大脑无法理解上面的解释吗?还是有什么东西让我错过了,让这个像玻璃一样清晰?
我确实知道在Objective-C中消息/接收器的概念,我只是对一个碰巧是
嗯,我认为可以用一个非常做作的例子来描述它。假设爪哇中有一个方法打印列阵列表中的所有元素:
1 2 3 4 5 6 | void foo(ArrayList list) { for(int i = 0; i < list.size(); ++i){ System.out.println(list.get(i).toString()); } } |
现在,如果您像这样调用该方法:someObject.foo(空);当它试图访问列表时,您可能会得到一个nullPointerException,在本例中是在对list.size()的调用中;现在,您可能永远不会用这样的空值调用someObject.foo(空)。但是,如果方法在生成arraylist时遇到错误(如someobject.foo(otherobject.getarraylist())时返回空值,则可能从该方法中获取arraylist;
当然,如果你这样做,你也会遇到问题:
1 2 | ArrayList list = NULL; list.size(); |
现在,在目标C中,我们有等效方法:
1 2 3 4 5 6 7 | - (void)foo:(NSArray*)anArray { int i; for(i = 0; i < [anArray count]; ++i){ NSLog(@"%@", [[anArray objectAtIndex:i] stringValue]; } } |
现在,如果我们有以下代码:
1 | [someObject foo:nil]; |
我们有相同的情况,Java将产生一个Null PoExtExchange。nil对象将首先在[anarray count]处访问,但是,不是抛出nullpointerException,Objective-C将根据上面的规则简单地返回0,因此循环将不会运行。但是,如果我们将循环设置为运行设置的次数,那么我们将首先在[Anarray ObjectAtindex:i]向Anarray发送一条消息;这也将返回0,但由于ObjectAtindex:返回一个指针,并且指向0的指针为nil/null,因此每次通过循环时,nslog都将为nil。(尽管nslog是一个函数而不是一个方法,但如果传递了nil nsstring,它将输出(空)。
在某些情况下,使用NullPointerException更好,因为您可以立即知道程序有问题,但除非捕获异常,否则程序将崩溃。(在C中,试图以这种方式取消对空值的引用会导致程序崩溃。)在Objective-C中,它只会导致可能不正确的运行时行为。但是,如果您有一个方法,如果它返回0/nil/null/Zeroed结构,它不会中断,那么这就避免了您必须检查以确保对象或参数为nil。
发送给
所有其他的帖子都是正确的,但也许这是重要的概念。
在Objective-C方法调用中,可以接受选择器的任何对象引用都是该选择器的有效目标。
这节省了很多"X类型的目标对象是吗?"代码-只要接收对象实现选择器,它与它是什么类完全没有区别!
这样做的原因是为了消除那些除了让编译器高兴之外什么都不做的猴子代码。是的,您可以得到更多方法调用的开销,但是您可以节省程序员的时间,这比CPU时间要昂贵得多。此外,您正在从应用程序中消除更多的代码和条件复杂性。
为落选者澄清:你可能认为这不是一个好方法,但它是如何实现语言的,它是Objective-C中推荐的编程习惯(见斯坦福iPhone编程讲座)。
它的意思是,当在nil指针上调用objc-msgsend时,运行时不会产生错误;相反,它返回一些(通常是有用的)值。可能有副作用的消息什么也不做。
它很有用,因为大多数默认值比错误更合适。例如:
1 | [someNullNSArrayReference count] => 0 |
也就是说,nil似乎是空数组。隐藏nil nsview引用不起任何作用。方便,嗯?
从Greg Parker的网站:
如果运行llvm编译器3.0(xcode 4.2)或更高版本
1 2 3 4 5 6 | Messages to nil with return type | return Integers up to 64 bits | 0 Floating-point up to long double | 0.0 Pointers | nil Structs | {0} Any _Complex type | {0, 0} |
在文档的引用中,有两个独立的概念——如果文档更清楚地说明这一点,可能会更好:
There are several patterns in Cocoa that take advantage of this fact.
The value returned from a message to nil may also be valid:
前者在这里可能更相关:通常能够向
1 2 3 4 5 6 | - (void)setValue:(MyClass *)newValue { if (value != newValue) { [value release]; value = [newValue retain]; } } |
如果向
后一点(从消息返回到
1 2 3 | if ([myArray count] > 0) { // do something... } |
这段代码同样不需要检查
尽管如此,能够向
这意味着,为了安全,通常不必检查任何地方是否有任何物体,尤其是:
1 | [someVariable release]; |
或者,如前所述,当您有一个nil值时,各种count和length方法都返回0,因此您不必为nil添加额外的检查:
1 | if ( [myString length] > 0 ) |
或者:
1 | return [myArray count]; // say for number of rows in a table |
我认为其他任何一个答案都没有提到这一点:如果你已经习惯了Java,你应该记住,虽然Mac OS X上的Objto-C有异常处理支持,但它是一个可选的语言特性,可以用编译器标志打开/关闭。我的猜测是,"向
我发布了一个类似的,但更长的回答,回答了相关的问题"是否断言在目标C中每个对象的创建都是必要的?"如果你想要更多的细节。
发送到nil且返回值的大小大于sizeof(void*)的objc消息在PowerPC处理器上生成未定义的值。除此之外,这些消息还会导致未定义的值返回到英特尔处理器上大小大于8字节的结构字段中。文森特·盖博在他的博客中很好地描述了这一点。
别想"接收器是零的";我同意,这很奇怪。如果你要向nil发送消息,就没有接收者。你只是在给什么都不发信息。
如何处理这是Java和Objtovi-C之间的哲学区别:在Java中,这是一个错误;在Objto-C中,它是NO-OP。
对于基元值,C不表示0,对于指针(在指针上下文中相当于0),则表示空。
Objective-C建立在C的无表示基础上,加上nil。nil是指向Nothing的对象指针。尽管在语义上不同于空值,但它们在技术上是等价的。
新分配的nsobjects的内容设置为0开始生命。这意味着对象必须指向其他对象的所有指针都以nil开头,因此不需要在in it方法中设置self.(association)=nil。
不过,nil最显著的行为是可以向其发送消息。
在其他语言中,如C++(或Java),这会使程序崩溃,但在ObjtoVc中,调用nIL的方法返回零值。这大大简化了表达式,因为它不需要在执行任何操作之前检查nil:
1 2 3 4 5 | // For example, this expression... if (name != nil && [name isEqualToString:@"Steve"]) { ... } // ...can be simplified to: if ([name isEqualToString:@"Steve"]) { ... } |
意识到nil在objective-c中的工作方式,可以将这种便利性作为一种特性,而不是应用程序中潜伏的bug。确保防止出现不需要nil值的情况,方法是检查并提前返回以静默失败,或者添加nsParameterAssert以引发异常。
来源:http://nshipster.com/nil/无/https://developer.apple.com/library/ios/documentation/cococa/conceptive/objectec/chapters/ocObjectsClasses.html(发送消息至nil)。