Objective-C是否对消息使用短路评估为零对象?

时间:2011-02-13 16:37:56

标签: objective-c short-circuiting

遵循通常的short-circuit evaluation question,对针对nil对象构建和发送的参数进行短路评估工作吗?例如:

NSMutableArray *nil_array = nil;
....
[nil_array addObject:[NSString stringWithFormat:@"Something big %@",
     function_that_takes_a_lot_of_time_to_compute()]];

是否会调用缓慢的函数,还是在不处理参数的情况下优化整个addObject调用?

2 个答案:

答案 0 :(得分:9)

始终将消息分派给对象指针,无论它指向对象还是指向nil。此外,消息在运行时发送,因此编译器不能只假设nil_array确实是nil并将其优化掉。如果初始化做了其他事情,nil_array原来是一个实例怎么办?

这意味着您将作为参数传递给您的方法的所有表达式都将被评估以便传递,因此不会发生任何类型的短路。您的慢速功能将被执行,如果它需要很长时间,它将影响您的程序的性能。

编辑:我刚刚为它做了一个小测试用例(空的Objective-C命令行程序)。如果你运行它并观察调试器控制台,你会发现所有三次调用function_that_takes_a_lot_of_time_to_compute()的输出都会出现(以5秒为间隔),而仅来自t1和{{1}的输出}} t3方法出现 - 当然,因为它们不是test:

<强>的main.m

nil

<强> Test.h

#import "Test.h"

int function_that_takes_a_lot_of_time_to_compute(void)
{
    static int i = 1;

    sleep(5);
    NSLog(@"%d", i);

    return i++;
}

int main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Test *t1 = [[Test alloc] init], *t2 = nil, *t3 = [[Test alloc] init];

    [t1 test:function_that_takes_a_lot_of_time_to_compute()];
    [t2 test:function_that_takes_a_lot_of_time_to_compute()]; // nil
    [t3 test:function_that_takes_a_lot_of_time_to_compute()];

    [t1 release];
    [t3 release];

    [pool drain];
    return 0;
}

<强> Test.m

@interface Test : NSObject {}

- (void)test:(int)arg;

@end

<强>输出

1
Testing arg: 1
2
3
Testing arg: 3

答案 1 :(得分:6)

接受的答案很好,但我想补充一下:

function_that_takes_a_lot_of_time_to_compute()+[NSString stringWithFormat:]会产生副作用,所以即使我们100%确定nil_arraynil(我们可以有时通过静态分析知道这一点,程序仍然必须执行function_that_takes_a_lot_of_time_to_compute()+[NSString stringWithFormat:]以确保它的行为符合预期。

如果函数f()没有副作用,则认为它是“纯粹的”。这意味着它可以接受输入参数并且可以返回一个值,但它从不调用任何非纯函数,也从不修改程序或全局内存的任何部分(传递参数和返回值所涉及的内存在此处不计算。例如,以下函数是“纯粹的”:

int munge(float foo, char bar) {
    unsigned short quux = bar << 4;
    return foo + quux;
}

C标准库中的纯函数示例包括memcmp()strlen()

当且仅当一个函数被认为是纯时,编译器可以安全地优化对它的调用,因为调用它对其余的没有影响该计划。但是,GCC对这样做是非常保守的,并且通常(总是?)只有当函数标记纯时才通过函数声明上的__attribute__((__pure__))修饰。

如果一个函数是纯函数,另外从不取消引用指针并且永远不会访问其堆栈帧之外的任何内存,它可以在GCC中标记为__attribute__((__const__)),这样就可以进行进一步的静态分析和优化。