言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

Objective-C の LL っぽい機能

折角マックがあるので、最近マックのプログラミングについて色々勉強しています。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 = (
)

見えない何かに対するアクセスが多い。

Cocoa は暗黙のうちに何かにアクセスするというのが多いような気がします。メモリ管理に使う NSAutoreleasePool * pool の pool もそうですし、画像コンテキストへのアクセスも、[[NSColor whiteColor] set]; のような感じで、画像コンテキストを引き数として渡さなくても良い事になっています。SDL のような画面が一つしか無いシステムならまだしも、Cocoa 見たいな複雑な物でこれでやって行けるのか心配になります。