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 に財産という意味があることを知ったのはアメリカに行って町を歩いたときです。←どうでもいいです!!

参考 http://developer.apple.com/jp/documentation/cocoa/Conceptual/ObjectiveC/Articles/chapter_5_section_3.html#//apple_ref/doc/uid/TP30001163-CH17-DontLinkElementID_16

@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アンアーカイブ

未分類メモ

構造体で先頭にisaがあれば、何でも Objective-C のオブジェクト。たとえば、C言語で構造体を作り、その先頭にisaとしてクラス定義へのポインタを置けば、それでもうObjective-Cのオブジェクトになる。

http://journal.mycom.co.jp/column/objc/017/index.html

更新日時:2012/08/23 12:28:26
キーワード:
参照:[計算機関連]