その他

その他

Miscellaneous - その他

main() から直接 Cocoa API を呼び出す

Keywords: Command line, NSAutoreleasePool

Classic な MacOS プログラミングと比べて、MacOS X のプログラミングのさいに、やっぱり便利なものはなにか?それは、標準のコマンドラインの存在だと思うんだ。もちろん、アプリケーションとして仕上げるときに使わないとしても、デバッグの段階では便利だよね。

でも、いざコマンドラインを使ってみると、問題があることに気付くと思うんだ。C や C++ の標準ライブラリ(stdio とか)だけのときは特に問題ない。でも、AppKit や Foundation Kit のクラスを使おうとすると、"_NSAutoreleaseNoPool()" っていうエラーコメントが表示されないかい?

AppKit ではメモリ管理に、参照カウンタを用いた、自動的にオブジェクトが削除される管理を行っている。それを行うのが NSAutoreleasePool だ。Project Builder でテンプレートを使ってアプリを作る場合は、自動的に NSAutoreleasePool が作られるから、気にしなくてよろしい。でも、コマンドラインのアプリを作るときは、自分で作らなくちゃいけないんだ。次のようにする。

Command line application (sample)

int main(int argc, const char* argv[]) {
 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 ...
 // Cocoa methods
 ...
 [pool release];
 return 0;
}

まず、NSAutoreleasePool のインスタンスを作る。そのあとで、AppKit や FoundationKit の関数を呼び出す。最後に NSAutoreleasePool の release を呼び出して、オブジェクトを削除する。これで OK だ。


Miscellaneous - その他

Class の名前を NSString で取り出す

Keywords: class, NSStringFromClass

ある id 型の変数があったとしよう。さて、こいつのクラスはいったい何か調べてみよう!まず、Class 型を表示する。id に代入されているものが、NSObject から派生されているものならば(たいていはそうだ)、class メソッドを使うことができる。class メソッドは、NSObject の protocol として、定義されている。

FoundationKit/NSObject.h

- (Class)class;

Class 型が得られたら、次はどうするか?こいつの名前を知りたいよね。そのためには、NSStringFromClass() という、そのものずばりの関数がある。

FoundationKit/NSObjCRuntime.h

FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);

これで、ある id 型のクラス名を表示させることができるぞ。

Display class name (sample)

void displayClass() {
 id anUnknownItem;
 Class unknownClass = [id class];
 NSString* classNameStr = NSStringFromClass(unknownClass);
 printf("class name is %s.¥n", [classNameStr cString]);
}




Miscellaneous - その他

オブジェクトの名前を C 文字列で取り出す

Keywords: NAMEOF, object_getClassName

オブジェクトのクラスの名前を表示するもう 1 つの方法。Objective-C runtime に用意されている NAMEOF マクロを使うんだ。

objc/obhc.h

#define NAMEOF(obj) object_getClassName(obj)

これは object_getClassName() っていう関数を呼び出している。この関数の定義はこうだ。

FoundationKit/NSObjCRuntime.h

OBJC_EXPORT const char *object_getClassName(id obj);

こいつがクラスの名前を返す。NSStringFromClass() との違いは、NSString 型で返すか、C string 型で返すか、っていうとこね。




Miscellaneous - その他

プロパティリストを変更可能な形に変える

Keywords: plist, NSMutableArray, NSMutableDictionary

プロパティリストを変更可能な形に変える形式は、NSArray や NSDictionary などでできてるリスト。便利なのは、こいつは文字列で保存しておくことができるんだ。ファイルからの読み出しも簡単。NSString の propertyList で変換できる。

だけど、こいつを使ってとりだすと、変更不可の形(immutable)で取り出されるんだ。これを変更可能(mutable)に変えてみよう。ここでは NSArray を NSMutable に、NSDictionary を NSMutableDictionary に変える。

(sample)

id MakeMutablePlistCopy(id plist)
{
 id copyPlist;

 if ([plist isMemberOfClass:[NSArray class]]) {
  copyPlist = MakeMutableArrayCopy(plist);
 }
 else if ([plist isMemberOfClass:[NSDictionary class]]) {
  copyPlist = MakeMutableDictionaryCopy(plist);
 }
 else {
  copyPlist = [plist copy];
 }

 return copyPlist;
}

NSMutableArray* MakeMutableArrayCopy(NSArray* array)
{
 id copy;
 int i;

  // Make copy
 copy = [[NSMutableArray alloc] initWithArray:array copyItems:YES];
 [copy autorelease];

 // Enumerate object
 for (i = 0; i < [copy count]; i++) {
  id object;

  object = [copy objectAtIndex:i];
  if ([object isMemberOfClass:[NSArray class]]) {
    [copy replaceObjectAtIndex:i
        withObject:MakeMutableArrayCopy(object)];
  }
  else if ([object isMemberOfClass:[NSDictionary class]]) {
    [copy replaceObjectAtIndex:i
        withObject:MakeMutableDictionaryCopy(object)];
  }
 }

 return copy;
}

NSMutableDictionary* MakeMutableDictionaryCopy(NSDictionary* dict)
{
 id copy;
 id key;
 NSEnumerator* enumerator;

 // Make copy
 copy = [[NSMutableDictionary alloc] initWithDictionary:dict copyItems:YES];
 [copy autorelease];

 // Enumerate object
 enumerator = [copy keyEnumerator];
 while (key = [enumerator nextObject]) {
 id object;

 object = [copy objectForKey:key];
 if ([object isMemberOfClass:[NSArray class]]) {
  [copy setObject:MakeMutableArrayCopy(object)
          forKey:key];
 }
 else if ([object isMemberOfClass:[NSDictionary class]]) {
  [copy setObject:MakeMutableDictionaryCopy(object)
          forKey:key];
  }
 }

 return copy;
}

やっていることは非常に簡単で、property list の中のオブジェクトを 1 つずつたどっていって、NSArray がでてきたら initWithArray:copyItems: で、NSDictionary が出てきたら initWithDictionary:copyItems: でコピーしてやるだけだ。これで、とりだした plist を編集できるよ。

■関連リンク:
プロパティリスト表示を取り出す、その逆も (NSDictionary)




Miscellaneous - その他

アプリケーションの実行パスを取得する

Keywords: bundlePath

Mac OS X では、アプリケーションはバンドルになっている。だから、実行中のアプリケーションのパスを調べたいときは、NSBundle を使う。NSBundle には、バンドルのディレクトリを調べる bundlePath や、実行ファイルのパスを調べる executablePath が用意されている。

Foundation/NSBundle

- (NSString*)bundlePath;
- (NSString*)executablePath;

次のように使えばいい。バンドルのディレクトリ、つまりアプリケーションファイル(.app ディレクトリ)のパスを調べるコードだ。

(sample)

 [[NSBundle mainBundle] bundlePath];





Miscellaneous - その他

ドキュメント・ベース・アプリケーションで、自動的に新規ファイルを開かない

Keywords: applicationShouldOpenUntitledFile:

(掲示板に書き込んでくれた、ななしさん、ありがとうございました。)

ドキュメント・ベース・アプリケーションは、デフォルトだと、アクティブになったときにドキュメントが開いていなかったら、自動的に新規ドキュメントを作成するでしょ。あれ、うざったいときがあるよね。これをやめさせるには、NSApplication の delegate で、applicationShouldOpenUntitledFile: を実装する。開きたくなかったら、NO を返せばいい。

(sample)

- (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender
{
 return NO;
}

NSApplication でファイルのオープンを処理しているからなんだろうけど、気づきにくいわな。