Objective-Cのselector

Thursday, March 22nd, 2012

Objective-Cのちょっと他には無い機能について、自分自身のメモのためにも書き留めておこうと思います。
まずは、selectorから。
C/C++の関数ポインタに近いですが、動的言語としての面も持ち合わせているObjective-Cらしい所もあり、Javaのリフレクション/イントロスペクションの方が近いかもしれません。

@interface MyClass : NSObject
-(void)hello;
@end

@implementation MyClass
-(void)hello
{
  NSLog(@"Hello");
}
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        SEL helloSelector = @selector(hello);

        MyClass *aObj = [[MyClass alloc] init];
        [aObj performSelector: helloSelector];
    }
    return 0;
}

MyClassというクラスにhelloというメソッドがあります。これを、通常のメッセージングではなく、selector経由で呼び出しています。そのためには、まずメソッドhelloのselectorを取得します。

SEL helloSelector = @selector(hello);

“@selector(メソッド名)”でselectorを取得し、SELという特別なselectorを格納するための型の変数を宣言し、そこに代入します。
selectorを用いたメソッド呼び出しは、NSObjectのperformSelectorを使います。

[aObj performSelector: helloSelector];

Javaのリフレクション/インストロスペクションに近いというと、文字列でメソッドを呼び出したいですが、NSSelectorFromStringを用いると文字列でselectorを生成できます。

SEL helloStringSelector = NSSelectorFromString(@"hello");

MyClass *aObj = [[MyClass alloc] init];
[aObj performSelector: helloStringSelector];

このSEL型ですが、おもしろいのはどのクラス定義とも関連づけられていなく、純粋なメソッドの定義から作られているということです。どういうことかというと、以下のように異なるMyClassとMyClass2に対しても、同じメソッド定義ならば、同じselectorが使えます。

@interface MyClass : NSObject
-(void)hello;
@end

@implementation MyClass
-(void)hello
{
  NSLog(@"Hello");
}
@end

@interface MyClass2 : NSObject
-(void)hello;
@end

@implementation MyClass2
-(void)hello
{
  NSLog(@"Hello");
}
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        SEL helloSelector = @selector(hello);

        MyClass *aObj = [[MyClass alloc] init];
        [aObj performSelector: helloSelector];
        MyClass2 *obj2 = [[MyClass2 alloc] init];
        [obj2 performSelector: helloSelector];
    }
    return 0;
}

Javaのリフレクションでは、java.lang.reflect.Methodは、クラス定義と紐づいているのでこういったことはできません。

そのためか、引数を持つメソッドの場合、引数を持つということも含めて指定してselectorを作成する必要があります。

@interface MyClass : NSObject
-(void)hi:(NSString*)name;
@end

@implementation MyClass
-(void)hi:(NSString*)name;
{
    NSLog(@"Hi, %@", name);
}
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        MyClass *aObj = [[MyClass alloc] init];

        SEL hiSelector = @selector(hi:);
        [aObj performSelector: hiSelector withObject: @"John"];

        SEL hiStringSelector = NSSelectorFromString(@"hi:");
        [aObj performSelector: hiStringSelector withObject: @"John"];
    }
}

@selector(hi:)のようにコロン(:)を一つ含めていることで、引数を一つ持つメソッドであることを宣言しています。引数が二つになればコロンが二つになります。

@interface MyClass : NSObject
-(void)hi:(NSString*)name name2:(NSString*)name2;
@end

@implementation MyClass
-(void)hi:(NSString*)name name2:(NSString*)name2
{
    NSLog(@"Hi, %@ and %@", name, name2);
}
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        MyClass *aObj = [[MyClass alloc] init];

        SEL hiTwoArgsSelector = @selector(hi:name2:);
        [obj2 performSelector: hiTwoArgsSelector withObject: @"John" withObject: @"Bill"];

        SEL hiTwoArgsStringSelector = NSSelectorFromString(@"hi:name2:");
        [obj2 performSelector: hiTwoArgsStringSelector withObject: @"John" withObject: @"Bill"];
    }
}