5

通常の短絡評価の質問に続いて、短絡評価は nil オブジェクトに対して構築および送信されたパラメーターに対して機能しますか? 例:

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

その遅い関数が呼び出されるのでしょうか、それともパラメーターを処理せずに addObject 呼び出し全体が最適化されるのでしょうか?

4

2 に答える 2

9

メッセージは、それがオブジェクトを指しているか、または を指しているかに関係なく、常にオブジェクト ポインターにディスパッチされますnil。さらに、メッセージは実行時に送信されるため、コンパイラはnil_array実際にそうであるnilと想定して最適化することはできません。初期化が別のことを行いnil_array、インスタンスであることが判明した場合はどうなるでしょうか?

つまり、メソッドに引数として渡すすべての式は、渡されるために評価されるため、いかなる種類の短絡も発生しません。遅い関数が実行され、時間がかかるとプログラムのパフォーマンスに影響します。

編集:私はそれのために小さなテストケースを作成しました(空のObjective-Cコマンドラインプログラム)。これを実行してデバッガ コンソールを観察すると、 への 3 つの呼び出しすべてからの出力function_that_takes_a_lot_of_time_to_compute()が (5 秒間隔で) 表示され、t1t3test:メソッドからの出力のみが表示されることがわかります。これらは ではないため、当然のこと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
テスト引数: 1
2
3
テスト引数: 3
于 2011-02-13T16:43:25.647 に答える
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()です。

関数が pure であることがわかっている場合にのみ、コンパイラはその関数への呼び出しを安全に最適化できます。これは、関数を呼び出さなくてもプログラムの残りの部分に影響がないためです。ただし、GCCはこれを行うことについて非常に保守的であり、一般的に(常に?)関数宣言の装飾を介して、関数が純粋であるとマークされている場合にのみ行います。__attribute__((__pure__))

関数が純粋であり、さらにポインタを逆参照せず、スタック フレーム外のメモリにアクセスしない場合は、代わり__attribute__((__const__))に GCC でマークすることができます。これにより、さらに静的な分析と最適化が可能になります。

于 2011-02-13T21:28:04.450 に答える