Objective-C 是否对 nil 对象的消息使用短路求值?

发布于 2024-10-17 03:10:19 字数 394 浏览 2 评论 0原文

按照通常的短路评估问题,进行短路评估适用于针对 nil 对象构建和发送的参数吗?示例:

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

该慢速函数是否会被调用,或者整个 addObject 调用是否会在不处理参数的情况下被优化?

Following the usual short-circuit evaluation question, does short-circuit evaluation work for parameters built and sent against nil objects? Example:

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

Is that slow function going to be called or will the whole addObject call be optimized out without processing the parameters?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

太傻旳人生 2024-10-24 03:10:19

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

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

编辑:我只是为它编写了一个小测试用例(空的 Objective-C 命令行程序)。如果您运行此命令并观察调试器控制台,您会注意到对 function_that_takes_a_lot_of_time_to_compute() 的所有三个调用的输出都会出现(以 5 秒为间隔),而仅来自 t1 的输出code> 和 t3test: 方法自然会出现,因为它们不是 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
Testing arg: 1
2
3
Testing arg: 3

A message is always dispatched to an object pointer, regardless of whether it points to an object or points to nil. Additionally, messages are sent in the runtime and therefore the compiler cannot just assume nil_array really is nil and optimize it away. What if the initialization did something else, and nil_array turns out to be an instance?

That means all the expressions you pass as arguments to your methods will be evaluated in order to be passed, so no short-circuiting of any sort happens. Your slow function will be executed, and if it takes a long time it'll affect the performance of your program.

EDIT: I just whipped up a little test case for the heck of it (empty Objective-C command line program). If you run this and observe the debugger console, you'll notice that output from all three calls to function_that_takes_a_lot_of_time_to_compute() appears (in 5-second intervals), while output from only t1's and t3's test: methods appears — naturally, since these are not nil.

main.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

Output

1
Testing arg: 1
2
3
Testing arg: 3
§对你不离不弃 2024-10-24 03:10:19

接受的答案是一个很好的答案,但我想补充一点:

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__)),这允许进一步的静态分析和优化。

The accepted answer is a good one, but I wanted to add:

function_that_takes_a_lot_of_time_to_compute() or +[NSString stringWithFormat:] can have side effects, so even if we knew with 100% certainty that nil_array was nil (and we can sometimes know this through static analysis), the program would still have to execute function_that_takes_a_lot_of_time_to_compute() and +[NSString stringWithFormat:] to ensure it was behaving as expected.

If a function f() has no side effects, it is considered "pure." This means that it can take input arguments and can return a value, but it never calls any non-pure functions and never modifies any part of the program or global memory (the memory involved in passing arguments and return values doesn't count here.) The following function, for instance, is "pure":

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

Examples of pure functions within the C standard library are memcmp() and strlen().

If and only if a function is known to be pure, the compiler could safely optimize away calls to it, since not calling it would have no effect on the rest of the program. However, GCC is very conservative about doing this, and generally (always?) does it only when a function is marked pure, via the __attribute__((__pure__)) decoration on the function declaration.

If a function is pure, and in addition never dereferences pointers and never accesses any memory outside its stack frame, it can instead be marked __attribute__((__const__)) in GCC, which allows even further static analysis and optimization.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文