レシーバの分岐とメッセージ送信を一行で書く【ダークサイドObjective-C】

異なるオブジェクトのいずれか一方に対して、メソッド呼び出しを行いたいケースがあります。

NSString *a = @"a", *b = @"b";
unichar c;

if (a != b) {
   c = [a characterAtIndex:0];
} else {
   c = [b characterAtIndex:0];
}

printf("%c", c); // 'a'

条件によって対象のレシーバは変わっても、送信するメッセージは同一となるようなケースです。

コードをよりシンプルにする

上記のコードは条件演算子(?:)を用いることで、以下のように簡潔に記述することもできますが、メッセージ式が二回出現してしまう点や、余計な一時変数を作らなければならなくなる点が難点です。

NSString *a = @"a", *b = @"b";
unichar c = a != b ? [a characterAtIndex:0] : [b characterAtIndex:0];
NSString *a = @"a", *b = @"b";
NSString *temp = a != b ? a : b;
unichar c = [temp characterAtIndex:0];

実は、プログラミング言語の仕組みを上手く活用することで、次のような記述が可能となります。

NSString *a = @"a", *b = @"b";
unichar c = [a != b ? a : b characterAtIndex:0];

このように、Objective-Cでは式の評価値に対するメッセージ転送も可能となっています。

戻り値に対するメッセージ転送

Objective-Cは、戻り値や評価結果に対するメッセージ呼び出しが可能です。

NSString *(^fn)(NSString *s) = ^(NSString *s) { return s; };
[fn(@"") length]; // 戻り値に対するメッセージ送信も可能

// この仕様はメソッドチェーンとしても応用されている
[[@"" substringToIndex:0] length];

ダークサイ度

条件演算子の評価値に対するメッセージ転送は、やりすぎない程度であれば、効果的なテクニックとなります。

ダークサイ度は★2つとします。メッセージ式の記法上、優先順位の問題にも遭遇しません。可読性が気になる場合は、条件演算子を括弧で囲うと良いでしょう。

[(a != b ? a : b) characterAtIndex:0];

適切に改行を入れると、読みやすくなる場合もあります。

[self.hoge
   ? self.foo
   : self.bar
   characterAtIndex:0];

状況によっては、一時変数を用いたほうが読みやすくなることもあります。

id tmp = _hoge ? _foo : _bar;
[tmp characterAtIndex:0];

このように、テクニックの利用に関する判断基準が曖昧な点や、記法にバラツキが発生しやすい点を考慮し、ダークサイ度は少し高めに設定しました。

広告