在Objective-C中向nil发送消息

在Objective-C中向nil发送消息,objective-c,Objective C,作为一名正在阅读苹果Objective-C2.0文档的Java开发人员,我想知道“向nil发送消息”意味着什么——更不用说它实际如何有用了。摘自文档: 可可有几种模式 他们利用了这个事实。这个 从消息返回的值为零 也可以是有效的: 如果该方法返回对象、任何指针类型、任何整数标量 大小小于或等于 sizeof(void*)、浮点数、双精度、双精度 长双人,或者长一长,然后是a 发送到nil的消息返回0 如果该方法返回Mac OS X ABI函数定义的结构 电话指南将在 注册,然后向nil发送一条

作为一名正在阅读苹果Objective-C2.0文档的Java开发人员,我想知道“向nil发送消息”意味着什么——更不用说它实际如何有用了。摘自文档:

可可有几种模式 他们利用了这个事实。这个 从消息返回的值为零 也可以是有效的:

  • 如果该方法返回对象、任何指针类型、任何整数标量 大小小于或等于 sizeof(void*)、浮点数、双精度、双精度 长双人,或者长一长,然后是a 发送到nil的消息返回0
  • 如果该方法返回Mac OS X ABI函数定义的结构 电话指南将在 注册,然后向nil发送一条消息 为字段中的每个字段返回0.0 数据结构。其他结构数据 类型将不会用零填充
  • 如果该方法返回除上述值以外的任何值 键入消息的返回值 发送到nil的未定义
Java让我的大脑无法理解上面的解释了吗?或者是我遗漏了什么东西,让它像玻璃一样清晰


在Objective-C中,我确实理解了消息/接收者的概念,我只是对碰巧是
nil

的接收者感到困惑,这意味着当对nil指针调用objc\u msgSend时,运行时不会产生错误;相反,它返回一些(通常有用的)值。可能有副作用的消息不会起任何作用

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

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

也就是说,nil似乎是空数组。隐藏nil NSView引用没有任何作用。Handy,呃?

好吧,我想可以用一个非常做作的例子来描述它。假设您在Java中有一个方法,可以打印ArrayList中的所有元素:

void foo(ArrayList list)
{
    for(int i = 0; i < list.size(); ++i){
        System.out.println(list.get(i).toString());
    }
}
现在,在Objective-C中,我们有了等效的方法:

- (void)foo:(NSArray*)anArray
{
    int i;
    for(i = 0; i < [anArray count]; ++i){
        NSLog(@"%@", [[anArray objectAtIndex:i] stringValue];
    }
}
我们有同样的情况,Java将生成NullPointerException。nil对象将首先在[anArray count]被访问,但是,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/a zeroed struct时不会中断,那么您就不必检查以确保对象或参数为nil。

这意味着通常不必到处检查nil对象以确保安全-尤其是y:

[someVariable release];
或者,如前所述,当您得到nil值时,各种count和length方法都返回0,因此您不必为nil all over添加额外的检查:

if ( [myString length] > 0 )
或者这个:

return [myArray count]; // say for number of rows in a table
不要认为“接收者为零”;我同意,这很奇怪。如果你向零发送消息,就没有接收者。你只是向零发送消息


如何处理这一点是Java和Objective-C之间的哲学区别:在Java中,这是一个错误;在Objective-C中,这是一个不可操作的问题。

在文档的引用中,有两个不同的概念——如果文档更清楚地说明这一点,可能会更好:

可可中有几种模式利用了这一事实

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

前者在这里可能更相关:通常能够将消息发送到
nil
使代码更简单——您不必到处检查空值。典型的示例可能是访问器方法:

- (void)setValue:(MyClass *)newValue {
    if (value != newValue) { 
        [value release];
        value = [newValue retain];
    }
}
如果向
nil
发送消息无效,此方法将更加复杂——在发送消息之前,您必须进行两次额外检查,以确保
value
newValue
不是
nil

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

[someNullNSArrayReference count] => 0
if ([myArray count] > 0) {
    // do something...
}
此代码同样不需要检查
nil
值,并且自然流动


综上所述,能够向
nil
发送消息的额外灵活性确实需要付出一定的代价。在某个阶段,您可能会以一种特殊的方式编写失败的代码,因为您没有考虑到值可能为
nil

而向
nil
发送消息的可能性nothing并返回
nil
nil
NULL
0
,或
0.0
,所有其他帖子都是正确的,但这里重要的可能是这个概念

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

这节省了大量“is the target object of type X?”代码-只要接收对象实现了选择器,它绝对不会改变它是什么类!
nil
是一个接受任何选择器的NSObject-它只是不做任何事情。这也消除了大量“检查nil,如果为true则不发送消息”代码。(“如果它接受它,它就实现它”的概念也允许您创建协议,这些协议是 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}
// For example, this expression...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }

// ...can be simplified to:
if ([name isEqualToString:@"Steve"]) { ... }