Objective-Cは非常に動的な言語です。
ランタイムを使って動的にメソッドを追加したり、といったこともできます。
そのために、コンパイル時に存在しないメソッドに対してもメッセージを送る方法が存在します。
(ただし、しっかり対処しないとエラーで落ちます)
これらの機能を実現しているのが転送のメカニズムです。
どういう手順で行われるか、後々役に立ちそうなのでメモとして残しておきます。
まず、大雑把に書くと以下の順にメソッドが呼び出されます。
+ (BOOL)resolveInstanceMethod:(SEL)sel
- (id)forwardingTargetForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)invocation
上から順に書いていきます。
resolveInstanceMethod: (resolveClassMethod:)
これはクラスメソッドになっていて、オブジェクトが理解できないメッセージがオブジェクトに対して渡されたときに呼び出されます。
(これはインスタンスメソッドの場合。名前から自明ですね。クラスメソッドの場合は類似のresolveClassMethod:
が呼ばれます)
返り値の型を見ると分かる通り、これは対応可能かどうかをBOOL
で返します。
そのため、このメソッド内で動的にメソッドを実装するなどする場合に使います。
(動的に追加したあとに、応答可能なことを示すためにYES
を返す、というわけです)
実際の例
// setter
void setter(id self, SEL _cmd, id value) {
//
}
// getter
NSString * getter(id self, SEL _cmd) {
return @"hoge";
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *selectorString = NSStringFromSelector(sel);
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, sel, (IMP)setter, "v@:@");
}
else {
class_addMethod(self, sel, (IMP)getter, "@@:");
}
}
ちなみにclass_addMethod
の最後の引数に変な文字列が渡されていますが、これは返り値と引数の状態を表した文字列です。意味は以下の通りです。
Type | 意味 |
---|---|
c | char |
i | int |
s | short |
l | long |
q | long long |
C | unsigned char |
I | unsinged int |
S | unsigned short |
L | unsigned lpng |
Q | unsigned long long |
f | float |
d | double |
B | C++のbool、またはC99の_Bool |
v | void |
* | 文字列 (char *) |
@ | オブジェクト(idとして定義されているもの) |
# | クラスオブジェクト(Class) |
: | メソッドセレクタ(SEL) |
[配列型] | 配列 |
{名前=型} | 構造体 |
(型...) | 共用体 |
bnum | numビットのフィールド |
^型 | 型へのポインタ |
? | 不明な型 |
r | const |
n | in |
N | inout |
o | out |
O | bycopy |
R | byref |
V | oneway |