@selectorの一意性

2つ連続してObjective-Cの話をしたのは

プログラマー天国のプログラマーのためのObjective-C入門のなかの一節、

「同じ名前のメソッドは、戻り値の型も引数の数と型も同じでなければならない」

うそや〜ん。マジで?

ってことがきっかけ。

で、まあ前の2本の記事のほか、HAPPY Macintosh Developing TIME!のObjective-Cの最適化なんかをみると分かるとおり、そんなことはなかった、あ〜よかった、ってのが結論。

確かに、@selectorではクラス名を渡さないので一体どうなってんだ?ってことなんだけど。

CocoaのObjective-Cコンパイラはライブラリファイル中のデータ領域(テキストセクション)にプログラム中の文字列定数をおくのだけど、そのなかに通常の文字列定数の他にセレクタの文字列も格納する。ほんでもってその文字列へのポインタを@selectorで取り出す。へぇ。

同じセレクタなら@selectorの値も同じになる、ということを考えると、ダイナミックリンクの際にリンカが同じ文字列のエントリが別々のライブラリファイルに存在したらどっちかをネグったりなんかする必要があると思うんだけど、実際どうなってるのかはちょっと不明。

横道にそれちゃったんで元に戻すと、各インスタンスには隠しメンバ変数としてisaというのがあり、こいつは自分自身のクラスへのポインタを持っている。自分自身のクラスってのもsingletonオブジェクト、ってのがC++と違うところ。

クラスのオブジェクトはC++の仮想関数テーブルのようなものを持っていて、この中から該当するセレクタを探す。C++みたいに直接仮想関数テーブルのエントリにアクセスするんじゃなくて、あくまで探す。探した後はキャッシュするんでいつでも遅いというわけではないんだけど。探すキーは@selectorの値だ。

で、そのクラスの関数テーブルに渡されたセレクタに相当するものがなければ、親クラスのほうに制御が移る。

てことで、「同じ名前のメソッドは、戻り値の型も引数の数と型も同じでなければならない」ってことはない。

そのクラスにその引数でそのセレクタを持つメソッドがあると確信できるなら、そのメッセージを送信して全然構わない。問題になるのは、そのセレクタを持つメソッドがないために基底クラスにそれがスルーされて、基底クラスではその引数じゃない引数を取ろうとしている場合だ。この場合は例えば引数のサイズや数が違ったりしたら、SIGBUSであっさり死亡してしまう。