Objective-C
Objective-C に関するメモ。
既存のクラスを動的に乗っ取るための手立て
Objective-C は「動的」というのが最大の特徴と言っても過言ではないとゆいたいです。 ここではソースすらない他人のかもしれないアプリの動いているクラスを乗っ取るための方法をまとめるです。
乗っ取るための下調べ
まずなんていう名前のクラスでなんて言う名前のメソッドがあってどんな引数をとっているかまで調べなければ、置き換えるためのメソッドを作ることすらできません。 幸い Objective-C だとそれを全部ランタイムで調べ上げることができます。Objective-C の真骨頂と言ってもいいですよね。
今までは木下誠さんのコラム「ダイナミック Objective-C」の http://journal.mycom.co.jp/column/objc/015/index.html などを大変参考にさせていただいておりましたが、 Mac OS X 10.6 Snow Leopard や Objective-C 2.0 でずいぶん変わったようです。 今 Snow Leopard や 2.0 で動くコードは以下です。
(たとえば class_nextMethodList はもうやめて class_copyMethodList を使うなど。Objective-C 2.0 から。参考: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/class_copyMethodList)
void showAllMethods(Class classObject) { unsigned int iterator = 0; Method *methodList = class_copyMethodList(classObject, &iterator); int count = 0; // メソッドを取得する int i; Method method; for (i = 0; i < iterator; i++) { // メソッドリストから、メソッドを取得する method = methodList[i]; // メソッド名と、引数の型を表示する printf(" %s, %s\n", method_getName(method), method_getTypeEncoding(method)); } printf(" count %d\n", count); }
void showAllClasses(void) { // 登録されているクラスの数を取得します int numClasses; numClasses = objc_getClassList(NULL, 0); if (numClasses == 0) return; // すべてのClassを取得します Class* classes; classes = malloc(sizeof(Class) * numClasses); numClasses = objc_getClassList(classes, numClasses); // クラス名を表示します int i; for (i = 0; i < numClasses; i++) { Class cls; cls = classes[i]; const char *name = class_getName(cls); if(name[0] == 'T'){ // たとえば Tで始めるクラスだけに絞ったりすれば Terminal.app を調べるのに便利 printf("--------------------------\n%s\n", name); showAllMethods(cls); } } free(classes); }
method_exchangeImplementations(Method are, Method kore)
method_exchangeImplementations(Method are, Method kore) で最終的にメソッド are を kore と入れ替える。 SIMBL プラグインでは、入れ替えるメソッドをつくり (従来の知らないクラスの直接カテゴリ拡張は x86_64 ランタイムでは禁止されたようなので NSObject のカテゴリを拡張する)、これで入れ替えるだけでよい。
クラス HGHoge のメソッド fuga: を hamu: に置き換える場合
Class class = objc_getClass("HGHoge"); Method method0 = class_getInstanceMethod(class, @selector(fuga:)); Method method1 = class_getInstanceMethod(class, @selector(hamu:)); method_exchangeImplementations(method0, method1);
とすればよい。
64ビット, x86_64 対応
カテゴリ (category) を使った知らんクラスのメソッド追加は禁止されたようだ。
Undefined symbols: "_OBJC_CLASS_$_乗っ取るクラス", referenced from: l_OBJC_$_CATEGORY_乗っ取るクラス_$_乗っ取るプロジェクト in 乗っ取るソースファイル.o __objc_classrefs__DATA@0 in 乗っ取るソースファイル.o ld: symbol(s) not found collect2: ld returned 1 exit status
なので乗っ取るクラスではなく NSObejct にカテゴリでメソッドを追加する。NSObject はみんな知っているし NSObject はルートクラスだからね。
インスタンス変数をゲットする
id delegate; object_getInstanceVariable(self, "delegate", (void**)&delegate);
これでインスタンス変数 delegete がゲットできる。
プロパティ
Objetive-C 2.0 で追加された便利記法、その名もプロパティ (property)。property に財産という意味があることを知ったのはアメリカに行って町を歩いたときです。←どうでもいいです!!
@interface 内
@property (retain) NSString* title;
属性 (attributes)
上記の retain の部分は属性と呼ばれます。 property の属性って property の properties なのかと思ってしまいますが attributes です。クラスの属性 (property) の持つ属性 (attribute) というわけですね。
- retain
- retainするんでしょう。よく手動で書いたもんですね。
- copy
- 代入するときにオブジェクトのコピーを使ってしまう
- nonatomic
- atomicだったら堅牢なアクセスけどこれだとたぶん気にせずがんがんアクセスしてパフォーマンス優先したいときに使うのかな (まだ勉強必要)
- readonly
- readonly
- readwrite
- readwrite可能
- setter=
- setterを自分で命名
- getter=
- getterを自ら命名
@implementation 内
@synthesize title;
とすると title のセッタとゲッタのメソッドが自動生成
ドット記法
ドット記法では型を静的にチェックされるので id でも NSImage などにキャストする必要がある (cf http://journal.mycom.co.jp/column/objc/103/index.html )
シリアライズとかなんとか
NSCoding protocol に準拠して NSCoder 見ながら
NSArchiver, NSKeyedArchiver | アーカイブ |
NSUnarchiver, NSKeyedUnarchiver | アンアーカイブ |
キーワード:
参照:[計算機関連]