折角マックがあるので、最近マックのプログラミングについて色々勉強しています。Objective-C の設計思想はかなり LL に毒されているとみえて、気持ち悪い機能が色々あります。忘れると厄介なのでメモします。
準備: Objective-C のコンパイル方法
-framework オプションを使ってライブラリを指定するとコマンドラインからコンパイル出来ます。
cc -o basic basic.m -framework Foundation && ./basic
nil はエラーにならない
Objective-C ではヌルポインタの事を nil と呼びますが。nil はどんなメソッドにも反応しないという約束になっています。例えば [nil message] のように書いてもエラーになりません。ヌル判定をしなくて良いので利点だそうです。
// Nil にメッセージを送っても無視される実験。 #import <Foundation/Foundation.h> @interface MyObj : NSObject {} @end @implementation MyObj - (char *) say { return "Hello!"; } @end int main(int argc, char *argv[]) { printf("nil say: %s\n", [nil say]); printf("MyObj say: %s\n", [[MyObj new] say]); return 0; }
出力
nil say: (null) MyObj say: Hello!
Key Value Coding (KVC) アクセッサの名前規則が LL っぽい。
KVC というのはプロパティ機能の事です。セッタやゲッタを作ると、実際の変数が無くてもあたかもそこに変数への参照があるような感じに振る舞う事が出来ます。面白いのが、特別なアクセッサを使うとさらにその参照が集合のように振る舞う事です。プロパティは普通自メンバへのアクセスを制御する物ですが、さらにメンバの要素へのアクセスも制御出来るのです。
下の例では、[mylist valueForKey:@"hoges"] という式によってmylist にある hoges というプロパティを探そうとします。普通は同名のインスタンス変数を探すのですが、変数が無くても countHoges とobjectInHogesAtIndex: というメソッドから、自動的に "HOGE" を 7 つ含むリストのように振る舞う何かを生成します。この気持ち悪いメソッド名もプロパティ名から決まっています。
// 配列として振る舞うプロパティの実験 #import <Foundation/Foundation.h> @interface MyList : NSObject {} @end @implementation MyList - (int) countOfHoges { return 7; } - (id) objectInHogesAtIndex:(int)index { return @"HOGE"; } @end int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // これが無いと何故か警告 id mylist = [MyList new]; id hoges = [mylist valueForKey:@"hoges"]; // hoges という名前の参照を探す NSLog(@"hoges = %@", hoges); [pool drain]; return 0; }
出力
2009-03-21 19:03:12.924 simpleKvc[95291:10b] hoges = ( HOGE, HOGE, HOGE, HOGE, HOGE, HOGE, HOGE )
KVC と書き込み可能な集合について
valueForKey: の代わりに mutableArrayValueForKey: を使うと、書き込めるリストっぽい物への参照を作る事が出来ます。その場合、insertObject:inHogesAtIndex: と removeObjectFromHogesAtIndex: の二つのメソッドを用意する事で、書き込みのアクセスをフック出来るので。undo の実装に使う事が出来ます。なお、このようなアクセッサが無い場合は勝手にインスタンス変数にアクセスしようとします。この場合何がおこるのか色々なメソッドの組み合わせで日が暮れるまで調べていたのですが、きりがないので諦めました。分かりやすいドキュメントを知っている人がいれば教えてください!
// 長さの変わる配列として振る舞うプロパティの実験 #import <Foundation/Foundation.h> @interface MyList : NSObject { @private NSMutableArray *hoges; } @end @implementation MyList - (id) init { [super init]; hoges= [NSMutableArray new]; return self; } - (id) hoges { return hoges; } - (void) insertObject:(id)object inHogesAtIndex:(int)index { NSLog(@"%@ が追加されようとしました!", object); [hoges insertObject: object atIndex: index]; } - (void) removeObjectFromHogesAtIndex:(int)index { NSLog(@"%i 番目が削除されようとしました!", index); [hoges removeObjectAtIndex: index]; } @end int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; id mylist = [MyList new]; id hoges = [mylist mutableArrayValueForKey:@"hoges"]; [hoges addObject:@"Hello world!"]; NSLog(@"hoges = %@", hoges); [hoges removeLastObject]; NSLog(@"hoges = %@", hoges); [pool drain]; return 0; }
出力
2009-03-21 19:39:38.111 arrayKvc[95883:10b] Hello world! が追加されようとしました! 2009-03-21 19:39:38.113 arrayKvc[95883:10b] hoges = ( "Hello world!" ) 2009-03-21 19:39:38.113 arrayKvc[95883:10b] 0 番目が削除されようとしました! 2009-03-21 19:39:38.114 arrayKvc[95883:10b] hoges = ( )