作为正在阅读Apple的Objective-C 2.0文档的Java开发人员:我想知道" 向nil发送消息 "意味着什么 - 更不用说它实际上有用了什么.摘自文档:
Cocoa中有几种模式可以利用这一事实.从消息返回到nil的值也可能是有效的:
如果该方法返回一个对象,任何指针类型,任何大小小于或等于sizeof(void*),float,double,long double或long long的整数标量,则发送给nil的消息返回0 .
如果该方法返回一个结构,如Mac OS X ABI函数调用指南所定义的那样在寄存器中返回,那么发送给nil的消息将为数据结构中的每个字段返回0.0.其他结构数据类型不会用零填充.
如果该方法返回除上述值类型之外的任何内容,则发送到nil的消息的返回值是未定义的.
Java是否让我的大脑无法解释上面的解释?或者是否有一些我想念的东西会让它像玻璃一样清晰?
我确实在Objective-C中得到了消息/接收器的概念,我只是对接收器碰巧感到困惑nil
.
好吧,我认为可以用一个非常人为的例子来描述.假设您在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,如果它遇到一些生成ArrayList的错误,如someObject.foo(otherObject.getArrayList());
当然,如果你这样做,你也会遇到问题:
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将根据上述规则返回0,而不是抛出NullPointerException,因此循环将不会运行.但是,如果我们将循环设置为运行一定次数,那么我们首先在[anArray objectAtIndex:i]向anArray发送一条消息; 这也将返回0,但由于objectAtIndex:返回一个指针,并且指向0的指针为nil/NULL,NSLog将每次通过循环传递nil.(虽然NSLog是一个函数而不是方法,但是如果传递了一个nil NSString,它会打印出来(null).
在某些情况下,有一个NullPointerException更好,因为您可以立即告诉程序有问题,但除非您捕获异常,程序将崩溃.(在C中,尝试以这种方式取消引用NULL会导致程序崩溃.)在Objective-C中,它只会导致可能不正确的运行时行为.但是,如果你有一个方法,如果它返回0/nil/NULL /归零结构不会中断,那么这将使你不必检查以确保对象或参数为零.
到的消息nil
不执行任何操作,并返回nil
,Nil
,NULL
,0
,或0.0
.
所有其他帖子都是正确的,但也许这就是重要的概念.
在Objective-C方法调用中,任何可以接受选择器的对象引用都是该选择器的有效目标.
这节省了很多"是X型的目标对象吗?" 代码 - 只要接收对象实现选择器,它就完全没有区别!nil
是一个NSObject接受任何选择器 - 它只是没有做任何事情.这消除了很多"检查零,不发送消息,如果是真的"代码.("如果它接受它,它实现它"概念也允许你创建协议,它有点像Java接口:一个声明,如果一个类实现了声明的方法,那么它符合协议.)
这样做的原因是消除除了让编译器满意之外什么也不做的猴子代码.是的,你得到了另外一个方法调用的开销,但是你节省了程序员时间,这是一个比CPU时间更昂贵的资源.此外,您还可以从应用程序中消除更多代码和更多条件复杂性.
澄清downvoters:你可能认为这不是一个好的方法,但它是语言的实现方式,它是Objective-C中推荐的编程习惯(参见Stanford iPhone编程讲座).
这意味着当在nil指针上调用objc_msgSend时,运行时不会产生错误; 相反,它返回一些(通常是有用的)值.可能具有副作用的消息不执行任何操作.
它很有用,因为大多数默认值比错误更合适.例如:
[someNullNSArrayReference count] => 0
即,nil似乎是空阵列.隐藏一个零NSView引用什么都不做.好的,嗯?
在文档的引文中,有两个独立的概念 - 如果文档更清晰,可能会更好:
Cocoa中有几种模式可以利用这一事实.
从消息返回到nil的值也可能是有效的:
前者可能在这里更相关:通常能够发送消息nil
使代码更直接 - 您不必在任何地方检查空值.规范示例可能是访问器方法:
- (void)setValue:(MyClass *)newValue { if (value != newValue) { [value release]; value = [newValue retain]; } }
如果发送消息nil
无效,则此方法会更复杂 - 您必须进行两次额外检查以确保value
并且在发送消息之前newValue
不会nil
.
然而,后一点(从消息返回的值nil
通常也是有效的)会给前者增加乘数效应.例如:
if ([myArray count] > 0) { // do something... }
此代码再次不需要检查nil
值,并自然流动...
所有这些都表明,能够发送消息的额外灵活性nil
确实需要付出一些代价.您可能会在某个阶段编写以特殊方式失败的代码,因为您没有考虑值可能存在的可能性nil
.
来自Greg Parker的网站:
如果运行LLVM Compiler 3.0(Xcode 4.2)或更高版本
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}
这意味着通常不需要为了安全而在任何地方检查零物体 - 特别是:
[someVariable release];
或者,如上所述,当你有一个nil值时,各种count和length方法都返回0,所以你不需要为nil添加额外的nil检查:
if ( [myString length] > 0 )
或这个:
return [myArray count]; // say for number of rows in a table
不要想"接收器是零"; 我同意,这是很奇怪的.如果您要向nil发送消息,则没有接收方.你只是发送消息给零.
如何处理这是Java和Objective-C之间的哲学差异:在Java中,这是一个错误; 在Objective-C中,它是一个无操作.
发送到nil并且其返回值的大小大于sizeof(void*)的ObjC消息在PowerPC处理器上产生未定义的值.除此之外,这些消息还会导致在Intel处理器上大小超过8个字节的结构字段中返回未定义的值.Vincent Gable在他的博文中很好地描述了这一点
我认为其他任何答案都没有明确提到这一点:如果你已经习惯了Java,你应该记住,虽然Mac OS X上的Objective-C有异常处理支持,但它是一个可选的语言功能,可以是用编译器标志打开/关闭.我的猜测是,这个设计的"将消息发送到nil
安全"早例外,在语言处理支持列入议程,并用一记类似的目标已完成:方法可以返回nil
指示错误,因为发送消息nil
通常返回nil
在转,这允许错误指示传播通过您的代码,因此您不必在每条消息上检查它.您只需要在重要的位置进行检查.我个人认为异常传播和处理是实现这一目标的更好方法,但不是每个人都同意这一点.(另一方面,我不喜欢Java要求你必须声明一个方法可能抛出的异常,这通常会迫使你在整个代码中语法上传播异常声明;但这是另一个讨论.)
我发布了一个类似但更长的回答相关问题"断言在Objective C中每个对象创建都成功了吗?" 如果你想要更多细节.