Objective-C2.0文法メモ セレクタ

メッセージング

Objective-Cではコンパイル時にメソッドのアドレスが確定している訳ではなく、メソッドのアドレスを各オブジェクト内のテーブルに記録しておいて、実行時にテーブル内を逐次検索して実行するべきアドレスを選択しています。メソッドを識別する最も単純な方法はメソッド実装とメソッド名(文字列)を対応されればよいですが、実際にはメソッド名と1対1に対応する識別子がコンパイル時にテーブルに記録されています。その実行するべきメソッド名に対応する識別子をセレクタと呼びます。セレクタはメソッド名にのみ依存しており、それが属しているオブジェクトについては識別しません。

オブジェクト
セレクタ メソッド実装
セレクタ メソッド実装
セレクタ メソッド実装

セレクタはobjc.hにてSEL型として定義されていて、@selectorを使って実装コード上でも扱うことができます。メソッドの関数ポインタを表から取り出して実行するように、セレクタを取得して実行時にメソッドを呼び出すことができます。

セレクタを取得してメソッドを実行する

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

@implementation MyClass
- (void) method
{
    NSLog(@"my class: method was called.");
}
@end

@interface YourClass:NSObject
- (void) method;
@end

@implementation YourClass
- (void) method
{
    NSLog(@"your class: method was called.");
}
@end

このメソッドのセレクタを取得するには、

    SEL mySelector = @selector(method);

特定のオブジェクトに対して取得したセレクタを実行するには、performSelectorメソッドを使います。

[object performSelector: mySelector];

今2つのクラスMyClass, YourClassに同じ名称のmethodが定義されているとします。セレクタを取得して双方にメッセージを送ってみた例です。

    MyClass *myobj = [[MyClass alloc]init];
    YourClass *yourobj = [[YourClass alloc]init];
    
    SEL mySelector = @selector(method);
    if ( [myobj respondsToSelector:mySelector] ) {
        [myobj performSelector: mySelector];
    }
    if ( [yourobj respondsToSelector:mySelector] ) {
        [yourobj performSelector: mySelector];
    }
    [myobj release];
    [yourobj release];
my class: method was called.
your class: method was called.

Target-Actionパターンでセレクタを利用する

CocoaではUI周りのイベントドリブンな処理を実現するのにTarget-Actionパターンと呼ばれる仕組みを利用しています。セレクタを利用すると、JavaのようにLinstenerクラスを作成しなくてもイベントとアクションの仕組みを簡単に実現することができます。
ボタンを押すと、レシーバーにメッセージが送信されるという例を考えてみます。ボタンクラスはターゲットとなるオブジェクトと、クリックしたときのアクションをセレクタとして保持しておきます。
Button.h

#import <Foundation/Foundation.h>

@interface Button : NSObject
{
    id target;
    SEL action;
}
- (void) onClick;
- (void) setTarget:(id)object Action:(SEL)selector;
@end

Button.m

#import "Button.h"
@implementation Button
- (void) onClick
{
    [target performSelector:action];
}
- (void) setTarget:(id)object Action:(SEL)selector
{
    target = object;
    action = selector;
}
@end

Receiver.h

@interface Receiver : NSObject
- (void) execute;
@end

Receiver.m

#import "Receiver.h"
@implementation Receiver
- (void) execute
{
    NSLog(@"action");
}
@end

セレクタを使うと、ボタンのアクションを簡単に設定することができます。イメージとしては実行する関数のポインタをターゲットともにイベントテーブルに登録する作業に似ていると思います。

    //  準備
    Receiver *receiver= [[Receiver alloc]init];
    Button *button = [[Button alloc]init];
    [button setTarget:receiver Action:@selector(execute)];
    //  クリック
    [button onClick];

    //  片付け
    [button release];
    [receiver release];
Receiver:execute