(前回)Cocoaの日々: Safari用独自プラグインを作る(1) - ひな形プロジェクト作成
Safari への組み込みが確認できたので実装に取り組もう。アプリケーションプログラムの挙動を変えたり機能を追加するには2つの方法があるようだ。
CocoaReverseEngineering - Wikir
- Posing
- Method Swizzling
Posing
Posing は +[NSOjbect poseAsClass:] を使い、任意のクラスで既存のクラスを「乗っ取る」ことでアプリケーションの挙動を変える。
例えば独自に NSWindowのサブクラスを用意して poseAsClass:を使う場合はこんな感じでできる。
[XCWindow poseAsClass:[NSWindow class]];
参考情報)
【コラム】ダイナミックObjective-C (12) ポージングで乗っ取り | エンタープライズ | マイコミジャーナル
ただ MacOSX10.5 からは Deprecated 扱いになったようだ。
Mac Dev Center: Deprecated NSObject Methods
Posing is deprecated in Mac OS X v10.5. The poseAsClass: method is not available in 64-bit applications on Mac OS X v10.5.
残念。
Method Swizzling
一方の Method Swizzling はクラスではなくメソッドを置き換える。swizzling はカクテルを混ぜる(作る)意味のようだ。
先の Wikir からポイントとなる箇所を引用する。
Method method = nil;
method = class_getInstanceMethod(_class, _oldSelector);
if (method == nil)
return NO;
method->method_name = _newSelector;
class_getInstanceMethod( ) は、インスタンスメソッド情報の構造体へのポインタを返す。
Objective-C プログラミング言語:class_getInstanceMethod
Method型については木下さんのコラムが詳しい。
【コラム】ダイナミックObjective-C (18) メソッドとは何か(1) - メソッド、セレクタ、メソッドの実装 | エンタープライズ | マイコミジャーナル
MacOSX10.5 環境で調べたところ Method の定義は下記にあった。
/Developer/SDKs/MacOSX10.5.sdk/usr/include/objc/runtime.h
typedef struct objc_method *Method;
objc_method の定義。
struct objc_method { SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; }
#OBJC2_UNAVAILABLE の意味はわからず。
Method Swizzling はこの method_name を書き換えることでメソッドの置き換えを行う。
具体的には、ターゲットのメソッド(SEL)をとっておき、独自クラスのメソッドに書き換える。こうすると本来呼び出されるメソッドの代わりに独自クラスのメソッドが呼び出されるようになる。
(イメージ)NSWindow の frame を置き換える。 (1) -[NSWindow frame] の Method を取得し、method_name を @selector(_nswindow_frame) へ変更する (2) 独自クラスのメソッド @selector(special_frame) を method_name へ設定する これで -[NSWindow frame] が呼び出された時、独自クラスのメソッド special_frame が呼び出される。
なお通常は独自メソッドの処理後に元のメソッドを呼び出す必要があるので、とっておいたメソッド(SEL)を最後に呼び出す。実際のコードは先のサイトを参照して欲しい(後日、検証でコードを各予定です)。
--------
(参考)メソッドのメタ情報へアクセスする関数はいろいろあってこれらも利用目的に応じて使えそうだ。
Objective-C プログラミング言語:メソッドへのアクセス
一部引用:
class_getInstanceMethod は、指定されたインスタンスメソッドについて記述しているデータ構造体へのポインタを返します。
class_getClassMethod は、指定されたクラスメソッドについて記述しているデータ構造体へのポインタを返します。
class_addMethods は、メソッドのリストをクラス定義に追加します。