作为正在阅读Apple的Objective-C 2.0文档的Java开发人员:我想知道“向nil发送消息”意味着什么 - 更不用说它实际上有用了什么。摘自文档摘录:
Cocoa有几种模式 利用这个事实。该 从消息返回的值为nil 也可能有效:
- 如果方法返回一个对象,任何指针类型,任何整数标量 大小小于或等于 sizeof(void *),float,double,a 长一倍,或长一长,然后一个 发送给nil的消息返回0。
- 如果方法返回结构,则由Mac OS X ABI函数定义 呼叫指南将被退回 寄存器,然后发送给nil的消息 为每个字段返回0.0 数据结构。其他结构数据 类型不会用零填充。
- 如果方法返回上述值以外的任何值 键入消息的返回值 发送到nil是未定义的。
Java是否让我的大脑无法解释上面的解释?或者是否有一些我想念的东西会让它像玻璃一样清晰?
我确实了解了Objective-C中的消息/接收器,我只是对接收器恰好是nil
感到困惑。
答案 0 :(得分:91)
嗯,我认为可以用一个非常人为的例子来描述。假设您在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)。但是,如果遇到某些生成ArrayList的错误(例如someObject.foo(otherObject.getArrayList());您可能已从一个返回NULL的方法获取了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,因此循环不会运行。但是,如果我们将循环设置为运行一定次数,那么我们首先在[anArray objectAtIndex:i]向anArray发送一条消息;这也将返回0,但由于objectAtIndex:返回一个指针,并且指向0的指针为nil / NULL,NSLog将每次通过循环传递nil。 (虽然NSLog是一个函数而不是一个方法,但是如果传递了一个nil NSString,它会打印出来(null)。
在某些情况下,有一个NullPointerException更好,因为您可以立即告诉程序有问题,但除非您捕获异常,程序将崩溃。 (在C中,尝试以这种方式取消引用NULL会导致程序崩溃。)在Objective-C中,它只会导致可能不正确的运行时行为。但是,如果你有一个方法,如果它返回0 / nil / NULL /一个零结构,不会中断,那么这将使你不必检查以确保对象或参数为零。
答案 1 :(得分:51)
发送给nil
的邮件不执行任何操作,并返回nil
,Nil
,NULL
,0
或0.0
。
答案 2 :(得分:40)
所有其他帖子都是正确的,但也许这就是重要的概念。
在Objective-C方法调用中,任何可以接受选择器的对象引用都是该选择器的有效目标。
这节省了很多“是X型的目标对象吗?”代码 - 只要接收对象实现选择器,它就绝对没有区别它是什么类! nil
是一个接受任何选择器的NSObject - 它不会做任何事情。这消除了很多“检查零,不发送消息,如果是真的”代码。 (“如果它接受它,它实现它”的概念也允许你创建协议,它们有点像Java接口:声明如果一个类实现了所述的方法,那么它符合协议。)
这样做的原因是消除除了让编译器满意之外什么也不做的猴子代码。是的,你得到了另外一个方法调用的开销,但是你节省了程序员时间,这是一个比CPU时间更昂贵的资源。此外,您正在从应用程序中消除更多代码和更多条件复杂性。
澄清downvoters:你可能认为这不是一个好方法,但它是语言的实现方式,而且是Objective-C 中推荐的编程习惯(参见Stanford iPhone编程)讲座)。
答案 3 :(得分:16)
这意味着当在nil指针上调用objc_msgSend时,运行时不会产生错误;相反,它返回一些(通常是有用的)值。可能有副作用的消息什么都不做。
这很有用,因为大多数默认值比错误更合适。例如:
[someNullNSArrayReference count] => 0
即,nil似乎是空数组。隐藏一个零NSView引用什么都不做。很方便,嗯?
答案 4 :(得分:12)
在文档的引文中,有两个独立的概念 - 如果文档更清晰可能会更好:
Cocoa中有几种模式可以利用这一事实。
从消息返回到nil的值也可能有效:
前者可能在这里更相关:通常能够向nil
发送消息使代码更直接 - 您不必在任何地方检查空值。规范示例可能是存取方法:
- (void)setValue:(MyClass *)newValue {
if (value != newValue) {
[value release];
value = [newValue retain];
}
}
如果向nil
发送邮件无效,则此方法会更复杂 - 您必须再进行两项检查以确保value
和newValue
不是{{ 1}}在发送消息之前。
后一点(从消息返回到nil
的值通常也是有效的),但是会给前者增加乘数效应。例如:
nil
此代码再次不需要检查if ([myArray count] > 0) {
// do something...
}
值,并自然流动......
所有这些说明,能够向nil
发送消息的额外灵活性确实需要付出一些代价。您可能会在某个阶段编写以特殊方式失败的代码,因为您没有考虑值可能为nil
的可能性。
答案 5 :(得分:12)
来自Greg Parker的site:
如果运行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}
答案 6 :(得分:9)
这意味着通常无需在任何地方检查零物品以确保安全 - 特别是:
[someVariable release];
或者,如上所述,当你有一个nil值时,各种count和length方法都会返回0,所以你不需要为nil添加额外的nil检查:
if ( [myString length] > 0 )
或者这个:
return [myArray count]; // say for number of rows in a table
答案 7 :(得分:6)
不要考虑“接收者是零”;我同意, 非常奇怪。如果您要向nil发送消息,则没有接收方。你只是发送消息。
如何处理这是Java和Objective-C之间的哲学差异:在Java中,这是一个错误;在Objective-C中,它是一个无操作。
答案 8 :(得分:6)
发送到nil并且其返回值的大小大于sizeof(void *)的ObjC消息在PowerPC处理器上产生未定义的值。除此之外,这些消息还会导致在Intel处理器上大小超过8个字节的结构字段中返回未定义的值。 Vincent Gable在他的blog post
中很好地描述了这一点答案 9 :(得分:6)
我认为其他任何答案都没有明确提到这一点:如果你已经习惯了Java,你应该记住,虽然Mac OS X上的Objective-C有异常处理支持,但它是一个可选的语言功能可以使用编译器标志打开/关闭。我的猜测是,这种“向nil
发送消息是安全的”设计早于在语言中包含异常处理支持,并且考虑到了类似的目标:方法可以返回nil
来指示错误并且由于向nil
发送消息通常依次返回nil
,这允许错误指示通过您的代码传播,因此您不必在每条消息中检查它。您只需要在重要的位置进行检查。我个人认为异常传播和处理是解决这一目标的更好方法,但不是每个人都同意这一点。 (另一方面,我不喜欢Java要求你必须声明一个方法可能抛出的异常,这通常会迫使你语法在你的代码中传播异常声明;但那是另一个的讨论。)
如果您需要更多详细信息,我已对相关问题"Is asserting that every object creation succeeded necessary in Objective C?"发布了类似但更长的答案。
答案 10 :(得分:2)
C表示原始值为0,指针为NULL(在指针上下文中等于0)。
Objective-C通过添加nil来构建C的无表示。 nil是一个没有任何东西的对象指针。虽然在语义上与NULL不同,但它们在技术上彼此相同。
newly-alloc'd NSObjects以其内容设置为0开始生命。这意味着对象对其他对象的所有指针都以nil开头,因此没有必要,例如,设置self。(association)= nil in init方法。
nil最值得注意的行为是,它可以向其发送消息。
在其他语言中,如C ++(或Java),这会使程序崩溃,但在Objective-C中,在nil上调用方法会返回零值。这大大简化了表达式,因为它避免了在执行任何操作之前检查nil的需要:
// For example, this expression...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }
// ...can be simplified to:
if ([name isEqualToString:@"Steve"]) { ... }
了解nil如何在Objective-C中工作,这使得这种便利成为一种功能,而不是应用程序中潜伏的错误。确保通过检查并提前返回静默失败,或者添加NSParameterAssert以引发异常来防止nil值不需要的情况。
来源: http://nshipster.com/nil/ https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html(发送消息为零)。