Objective-Cの残念な子なところ

__blockがうざすぎる。

NSArray* a = [object foo];
NSArray* b = [a bar];
NSArray* c = [b baz];

とかあったりして、ここいらがどうも遅い気がするので時間を計測したいとしよう。まずはこんな感じ。

NSDate* start = [NSDate date];

NSArray* a = [object foo];
NSArray* b = [object bar];
NSArray* c = [object baz];

NSLog(@"%g", -[start timeIntervalSinceNow]);

やっぱり遅いようなんだけど、はたしてfoobarbazのどれが遅いんだろうか。

NSDate* start = [NSDate date];

NSArray* a = [object foo];
NSLog(@"%g", -[start timeIntervalSinceNow]);

NSArray* b = [a bar];
NSLog(@"%g", -[start timeIntervalSinceNow]);

NSArray* c = [b baz];
NSLog(@"%g", -[start timeIntervalSinceNow]);

これで、なんとなくどれが遅いのかはわかるようになった。

さて、このコードはどう考えてもかっこわるいので、もうちょっときれいにしたいと思います。Benchmarkクラスを適当に追加して、こんなメソッドを追加しましょう。

+ (void)run:(NSString*)message block:((void)(^)())block {
  NSDate* start = [NSDate date];
  block()
  NSLog(@"%@ in %gsecs", message, -[start timeIntervalSinceNow]);
}

さあ、これで実行時間を計測するのがかっこよくなりますね!

[Benchmark run:"foo" block:^{ NSArray* a = [object foo]; }];
[Benchmark run:"bar" block:^{ NSArray* b = [a bar]; }];
[Benchmark run:"baz" block:^{ NSArray* c = [b baz]; }];

これではダメですね。ブロックの中で宣言された変数は、そのブロックの中からしかアクセスできないので、外に宣言しないといけません。

NSArray* a;
NSArray* b;
NSArray* c;
[Benchmark run:"foo" block:^{ a = [object foo]; }];
[Benchmark run:"bar" block:^{ b = [a bar]; }];
[Benchmark run:"baz" block:^{ c = [b baz]; }];

まだダメです。外のローカル変数に代入するには、__blockをつけないといけません。

__block NSArray* a;
__block NSArray* b;
__block NSArray* c;
[Benchmark run:"foo" block:^{ a = [object foo]; }];
[Benchmark run:"bar" block:^{ b = [a bar]; }];
[Benchmark run:"baz" block:^{ c = [b baz]; }];

これでやっと動くようになります。


これがとてもめんどくさい。しかも、これは計測のためのコードなので、ボトルネックが判明して対策をとったあとに、最初のコードに戻すなりなんなりが必要になるのです。とてもめんどくさい。とてもめんどくさい。

こういうめんどくささがあると、コードをいじるためのコストが大きくなって、よりよいコードを探すための手間を惜しむようになってしまい、残念なコードが散見されるようになってしまう。「試行錯誤しやすさ」というのを一つの言語の性能として考えるべき、という話はけっこう良く聞く*1ところだと思います。

どうもObjective-Cというのは俺が思っていた以上に、この点での性能が良くない子みたいですね。いや、他にもいろいろあるのはわかっていますが。というか、こんなあほみたいな問題があるとは思ってなかった……

なにかうまい方法があるのでしょうか?

例によって宣伝の時間です

株式会社ユビレジでは、こんな残念な子も見捨てることなく、我慢強く試行錯誤に取り組んでくれるソフトウェアエンジニアを募集しています。

なんか適当に@に連絡するか、あるいはTwitterで「ユビレジで働きたい!」って書いてくれれば弊社の代表が検索してるのでそのうち見つけると思います。

*1:shiroさんとこで見たんだと思うけど、ちょっと探したけど出てこなかった……