【问题标题】:Does Objective-C use short-circuit evaluation for messages to nil objects?Objective-C 是否对 nil 对象的消息使用短路评估?
【发布时间】:2011-06-26 12:04:36
【问题描述】:

按照通常的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 调用?

【问题讨论】:

  • 太害怕调试器发现自己? :-)
  • 我本来打算自己回答的,因为我发现它很有趣,但是 BoltClock 打败了我,所以他得到了分数(但是我必须等待 3 分钟才能接受他的回答,啊,垃圾邮件过滤器)。

标签: objective-c short-circuiting


【解决方案1】:

消息总是被分派给一个对象指针,不管它是指向一个对象还是指向nil。此外,消息是在运行时发送的,因此编译器不能仅仅假设 nil_array 真的是 nil 并对其进行优化。如果初始化做了其他事情,而 nil_array 原来是一个实例怎么办?

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

编辑:我刚刚为它准备了一个小测试用例(空的 Objective-C 命令行程序)。如果您运行它并观察调试器控制台,您会注意到对function_that_takes_a_lot_of_time_to_compute() 的所有三个调用的输出都会出现(以 5 秒为间隔),而仅来自t1t3 的输出@987654328 @ 方法出现了——很自然,因为这些不是nil

ma​​in.m

#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.h

@interface Test : NSObject {}

- (void)test:(int)arg;

@end

Test.m

@implementation Test

- (void)test:(int)arg
{
    NSLog(@"Testing arg: %d", arg);
}

@end

输出

1 测试参数:1 2 3 测试参数:3

【讨论】:

    【解决方案2】:

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

    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()

    当且仅当已知函数是纯函数,编译器可以安全地优化对它的调用,因为 not 调用它不会影响其余的的程序。但是,GCC 对此非常保守,通常(总是?)仅在函数被标记纯函数时才这样做,通过函数声明上的__attribute__((__pure__)) 装饰。

    如果一个函数是纯函数,并且从不取消引用指针并且从不访问其堆栈框架之外的任何内存,则可以在 GCC 中将其标记为 __attribute__((__const__)),从而允许进一步的静态分析和优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-28
      • 2020-06-30
      • 2012-02-10
      • 2021-09-23
      相关资源
      最近更新 更多