メソッド名合成を使ったプログラミング(1)

Objective-Cでは文字列型NSStringとセレクタ型SELを相互に変換出来る。この変換機能で実行時に生成した文字列からメソッドを呼び出す事が可能になる。

スクリプト言語では当たり前の機能だが、コンパイル言語でこのような機能があるのは少ない。

ここで、文字列からメソッド名を合成する事、その合成したメソッドを積極的に使ったコーディングをそれぞれ、「メソッド名合成」及び「メソッド名合成プログラミング」と命名する事にする。

メソッド名合成

文字列型NSStringとセレクタ型SELの相互変換関数として、以下の2つがある。

NSString *NSStringFromSelector(SEL aSelector);
SEL NSSelectorFromString(NSString *aSelectorName);

文字列からSEL型を作るのはもちろん、SEL型も文字列型に変換後は編集可能になる。編集した文字列型を再びSEL型に戻せば、SEL型の編集も可能と見なせる。

単純な例として、プロパティ名からsetterメソッドを合成する関数を示す。

SEL setterSelectorFromName(NSString* inName)
{
    NSString* theResultString;

    theResultString = [NSString stringWithFormat:@"set%@:",
                    [inName stringByReplacingCharactersInRange:NSMakeRange(0,1)
                                                    withString:[[inName substringToIndex:1]
                                                                uppercaseString]]];

    return NSSelectorFromString(theResultString);
}

この関数は、@”test”を渡すと@selector(setTest:)を返す動作をする。

合成したメソッドの呼び出し

プロトコルや非公式プロトコルのメソッドと同じように、合成したメソッドは対象となるオブジェクトにメソッドの実装があるとは保証されていない。

よって、セレクタが呼び出し可能かを確認後呼び出しをしなければならない。

コードとしては、以下のようになる。

if( [self respondsToSelector:theSelector] )
{
    [self performSelector:theSelector
               withObject:theObject];
}

これはデリゲートやプロトコルを使ったプログラミングと同じようになる。

すこしだけ異なるのは、プロトコルと違いメソッド名は動的に決まるので、[NSObject performSelector:]やNSInvocationを使ったメソッド呼び出しを行う事になる。

メソッド名合成プログラミング

メソッド名合成プログラミングの応用例がCocoaのフレームワークの中にある。 ここでは、判り易い例としてkey値コーディングのインディックス付きアクセッサを示す。

インディックス付きアクセッサは以下の2つのメソッドをサポートする必要がある。

- (NSInteger) countOf_KEY_;
- (id) objectIn_KEY_AtIndex:(NSInteger)index;

ここで、”_KEY_”はkey値コーディングでいうキー文字列が入る。

この2つのメソッドを定義すると、そのオブジェクトはkey値コーディング上ではプロパティ_KEY_を持っているように振る舞う事が出来る。

プロトコルと異なり、_KEY_は任意の文字列に出来る。実行時に対で実装されていれば実行出来る。

同じような例として、プロパティ値の検証用メソッドがある。

- (BOOL) validate_KEY_:(id*)ioValue
                 error:(NSError**)outError;

ここでも、”_KEY_”はkey値コーディングでいうキー文字列が入る。

これらの例のように、Cocoaでは「メソッド名合成を使ったプログラミング」は既に使われている。

つづく。