Использует ли Objective-C оценку короткого замыкания для сообщений на нулевые объекты? - PullRequest
5 голосов
/ 13 февраля 2011

Следуя обычному вопросу оценки короткого замыкания , работает ли оценка короткого замыкания для параметров, созданных и отправленных для нулевых объектов? Пример:

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

Будет ли вызываться эта медленная функция или весь вызов addObject будет оптимизирован без обработки параметров?

Ответы [ 2 ]

9 голосов
/ 13 февраля 2011

Сообщение всегда отправляется указателю объекта, независимо от того, указывает оно на объект или указывает на nil. Кроме того, сообщения отправляются во время выполнения, и поэтому компилятор не может просто предположить, что nil_array действительно равен nil, и оптимизировать его. Что если инициализация сделала что-то еще, а nil_array окажется экземпляром?

Это означает, что все выражения, которые вы передаете в качестве аргументов своим методам, будут оцениваться, чтобы их можно было передать, поэтому никакого короткого замыкания не происходит. Ваша медленная функция будет выполнена, и если она займет много времени, это повлияет на производительность вашей программы.

РЕДАКТИРОВАТЬ: Я только что сделал небольшой тестовый пример для проверки (пустая программа командной строки Objective-C). Если вы запустите это и увидите консоль отладчика, вы заметите, что вывод всех трех вызовов на function_that_takes_a_lot_of_time_to_compute() появляется (с 5-секундным интервалом), а вывод только на t1 и t3 * test: методы появляются - естественно, так как это не 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

выход

1
Testing arg: 1
2
3
Testing arg: 3
6 голосов
/ 14 февраля 2011

Принятый ответ хороший, но я хотел бы добавить:

function_that_takes_a_lot_of_time_to_compute() или +[NSString stringWithFormat:] могут иметь побочные эффекты, поэтому даже если мы знали со 100% уверенностью, что nil_array было nil (а мы можем иногда знать это через статический анализ), программе все равно придется выполнить function_that_takes_a_lot_of_time_to_compute() и +[NSString stringWithFormat:], чтобы убедиться, что она работает так, как ожидалось.

ЕслиФункция f() не имеет побочных эффектов, она считается «чистой».Это означает, что он может принимать входные аргументы и может возвращать значение, но он никогда не вызывает какие-либо не чистые функции и никогда не изменяет какую-либо часть программы или глобальную память (память, участвующая в передаче аргументов и возвращаемых значений, здесь не учитывается.) Следующая функция, например, «pure»:

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

Примерами чистых функций в стандартной библиотеке C являются memcmp() и strlen().

Еслии только если известно, что функция является чистой , компилятор может безопасно оптимизировать вызовы к ней, поскольку , а не , вызывающая ее, не будет влиять на остальную часть программы.Однако GCC очень консервативен в этом и обычно (всегда?) Делает это только тогда, когда функция помечена как помечена как , через декорацию __attribute__((__pure__)) в объявлении функции.

Еслифункция является чистой и, кроме того, никогда не разыменовывает указатели и никогда не обращается к какой-либо памяти за пределами своего стекового фрейма; вместо этого ее можно пометить __attribute__((__const__)) в GCC, что позволяет проводить еще более статический анализ и оптимизацию.

...