[ ホーム | お知らせ | ソフトウェア | 覚え書き | メール | ->英語 ] |
OPENSTEPの覚え書き
OPENSTEPのプログラムを開発する際に作者が調べた事柄を書き留めました。
ほとんどのトピックは「テンでテンてこまい」に引っ越しました。
"(居残り)"で始まるトピックは引越ししなかったものです。
-(id)init { if( self=[super init]) { // 初期化の手続き // ... } return self; } -(void)dealloc { // 解放の手続き // ... [super dealloc]; }
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { // filenameで指定されたファイルを開く、成功したらYESを返す return YES; } - (void)applicationWillFinishLaunching:(NSNotification *)aNotification { // アプリケーション起動直前の処理 } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // アプリケーション起動直後の処理 } - (BOOL)applicationShouldTerminate:(NSApplication *)sender { // アプリケーションを終了してもよいなら、YESを返す return YES; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { // アプリケーションを終了してもよいなら、YESを返す return YES; } - (void)applicationWillTerminate:(NSNotification *)aNotification { // アプリケーション終了直前の処理 }
FileOwner.m: -(id)init { BOOL f; if( self=[super init]) { // 初期化の手続き // ... f=[NSBundle loadNibNamed:(NSString*)nibFileName owner:(id)self]; // nibファイル読み込み成功ならYESを返す } return self; }
- (BOOL)windowShouldClose:(id)sender { // ウィンドウを閉じてもよいなら、YES
return YES; } - (void)windowWillClose:(NSNotification *)aNotification { // ウィンドウクローズ直前の処理 }
NSTableView、データソースのプロトコル
//データを参照 - (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
id theRecord, theValue;
NSParameterAssert(rowIndex >= 0 && rowIndex < [records count]);
theRecord = [records objectAtIndex:rowIndex];
theValue = [theRecord objectForKey:[aTableColumn identifier]];
return theValue;
} //データを書き込み
- (void)tableView:(NSTableView *)aTableView setObjectValue:anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
id theRecord;
NSParameterAssert(rowIndex >= 0 && rowIndex < [records count]); theRecord = [records objectAtIndex:rowIndex]; [theRecord setObject:anObject forKey:[aTableColumn identifier]]; return; }
//データ数を返す
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [records count];
}
foo.m: ← NSDocumentのサブクラス - (void)windowControllerWillLoadNib:(NSWindowController *)windowController{ // Nibファイル読み込み完了直前 } - (void)windowControllerDidLoadNib:(NSWindowController *) aController{ [super windowControllerDidLoadNib:aController]; // Nibファイル読み込み完了直後の処理 } - (BOOL)shouldCloseWindowController:(NSWindowController *)windowController{ // WIndowControllerを解放してもよいなら、YES return YES; } - (BOOL)canCloseDocument{ // Documentを閉じてもよいなら、YES return YES; } - (void)close { // ドキュメントクローズの処理 [super close]; } - (NSString *)windowNibName { // このドキュメントクラスが使うNibファイル名を返す return @"Document"; } - (NSData *)dataRepresentationOfType:(NSString *)aType { // このドキュメントが使うデータを返す return nil; } - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType { // このドキュメントが使うデータが取得できたなら、YES // 取得した値は、dataとaTypeの中に return YES; }
アイコンのデータ形式
解像度 72dpi 大きさ 48pixel x 48pixel 色 gray/alphaで各2bit(グレースケール) red/green/blue/alphaで各4bit(カラー)
プロトコルにはこんな利点も
プロトコルで定義しているインターフェースに関しては、 そのプロトコルを採用しているクラスの インターフェース(@interface)を記述する必要はない → インターフェースの仕様に変更があった場合、 プロトコルを変更するだけでよい (そのプロトコル採用しているクラスのインターフェースは 一斉に変更することになり、コーディングの手間が減る) → ただしインプリメンテーション(@implementation)の定義は すべて書き換える必要がある
メッセージの転送について
受信したメッセージに対応するメソッドがない場合、 -(void)doesRecognizeSelector:(SEL)が呼び出される このメソッドをカスタムオブジェクトでオーバーライドしている場合、 そのメソッドの呼び出し終了後、 -(void)forwardInvocation:(NSInvocation*)invが呼び出される。 -forwardInvocationでは 受信したメッセージのセレクタは分からないため、 メッセージの種類に応じて動的に転送先を変えることはできない?
名前からクラス、メソッド、インスタンス変数を取得するには?
[OPENSTEP関数] ・クラス名→クラス Class NSClassFromString(NSString*) 名前に一致したクラスがない場合はゼロが返される。 ・クラス→クラス名 NSString* NSStringFromClass(Class) ・メソッド名→メソッド SEL NSSelectorFromString(NSString*) この関数を発行したインスタンスの中に 名前に一致したメソッドがない場合はゼロが返される。 ・メソッド→メソッド名 NSString* NSStrinfFromSelector(SEL) 名前に一致したクラス、メソッドがない場合はゼロが返される。 [ローレベルな関数] SEL sel_getUid( const char*) メソッド名からすでに登録済みのエントリを取得 NSSelectorFromStringに相当 ただし他クラスのメソッドのエントリも取得可 SEL sel_registerName( const char*) 指定したメソッド名のエントリを新規に取得 const char* sel_getName( SEL) エントリからメソッド名を取得 NSStringFromSelectorに相当 Ivar class_getInstanceVariables(Class,char*) Ivar object_getInstanceVariable(id,char*,void*) Ivar object_setInstanceVariable(id,char*,void*)
インスタンス変数の保有と更新、参照
id型をもつインスタンス変数について [インスタンス変数の保有] クラス内で生成したインスタンス変数は 基本的に、そのクラス自身に保有と解放の責任がある。 -init { [super init]; … anIVar=[[NSNumber numberWithInt:1] retain]; … return self; } -(void)dealloc { … [anIVar release]; … [super dealloc]; } [インスタンス変数の更新] インスタンス変数を更新するには いくつかの手段が用意されている。 (a) 現在保持しているインスタンス変数を解放し、 新しく与えられたオブジェクトへの参照を保持する。 (b) 現在保持しているインスタンス変数を解放し、 与えられたオブジェクトのコピーを保持する。 この場合、外部オブジェクトから内容は変更できない。 (c) (b)では現在保持しているインスタンス変数の内容と 与えられたオブジェクトの内容がまったく同じ場合に エラーが発生する場合がある。 (c)は(b)にその対策を施したもの。 -init { iVar=[[Foo foo] retain]; return self; } -(void)dealloc { [iVar release]; [super dealloc]; } -(void)setIVar:(Foo*)newIVar <-(a) { [iVar autorelease]; iVar=[newIVar retain]; } -(void)setIVar:(Foo*)newIVar <-(b) { [iVar autorelease]; iVar=[newIVar copy]; } -(void)setIVar:(Foo*)newIVar <-(c) { Foo *oldIVar=iVar; iVar=[newIVar copy]; [oldIVar release]; } [インスタンス変数の参照] retainの必要があるのは、 外部オブジェクトから与えられたオブジェクトの参照を そのオブジェクトの参照を得たスコープの外でも持ち続ける場合。 -foo { iVar=[[NSNumber numberWithInt:1] retain]; <-(a) } -foo { NSNumber *obj=[NSNumber numberWithInt:1]; <-(b) [array addObject:obj]; } (a)では新規作成したNSNumberのインスタンスをインスタンス変数として 保持するのでretainが必要。 (b)ではNSNumberのインスタンスはarrayが保持するのでretainの必要なし。 つまり、 NSNumberのインスタンスを作成した時点ではobjのretainCountは1だが、 addObjectの後ではretainCountは2になる。 addObjectしなければobjはfooを抜けた時点で自動的に解放される。 したがって、 objを他のメソッドなどで参照する場合はretainしなければならないが、 この場合objは arrayに新規作成したNSNumberのインスタンスを格納するまでの 一時的な変数なので、retainCountをここで増やす必要はない。
Machカーネル関数とC-Thread関数
[Machカーネル関数とC-Thread関数] Machカーネル関数 基本的なスレッド制御 C-Thread関数 高度なスレッド制御 C-Thread関数では スレッドのForkとJoin、排他制御と同期処理のしくみが用意されている。 [スレッド情報の取得に関連するMachカーネル関数] host_t host_self ホストポートの取得、読み取り専用 host_t host_priv_self ホストポートの取得、スーパーユーザーでのみ実行可能 task_self thread_self processor_set_default host_prossor_set_priv processor_set_threads
マルチスレッドに関連したクラス
[関連クラス] NSThread NSConnection NSRunLoop [NSThreadについて] スレッドの実行を制御できない? 実行中かの判断、実行の中断、再開、強制終了など。 [ロックに関連したクラス] 複数のスレッドで同一データ、オブジェクトを共用する場合には つぎに示すロック機能を持ったクラスを活用する。 NSLock 実行制御の移行をブロックしないロック機能をもつ NSConditionLock 条件が満たされるまで実行制御の移行を延期するロック機能をもつ NSRecursiveLock デッドロックを発生させることなく 単一のスレッドが複数回数ロックを所得できるロック機能をもつ
主要なキー操作
106キーボードでのNeXTキー配列では、 特殊用途で用いるキーは次のように割り当てられる。 NeXTキー配列 106キーボード ---------------------------- [Esc] [半角/全角] [Tab] [Tab] [Cntrl]* [CapsLock] [Shift] [Shift] [Alt]* [Cntrl] [Command]* [Alt] ここでは、 *のついたNeXTキー配列のキーはNeXTキー配列の表記で、 それ以外のキーは106キーボード配列の表記で操作を記述する。 (キーの割り当て) [Insert] 音量Up [Delete] 音量Down [F1] ヘルプの起動 [Alt]+[Contrl] ピックアップヘルプ [Shift] -> [Alt] CapsのOn/Off (GUIの操作) [Command]+[↑] 最後面のウィンドウを最前面へ [Command]+[↓] 最前面のウィンドウを最後面へ [Alt]+"タイトルバーのクリック" クリックしたウィンドウを最前面へ [Command]+"タイトルバーのクリック" クリックしたウィンドウを最後面へ [Alt]+"スクロールボタンのクリック" 1画面分のスクロール [Alt]+"スクロールノブのドラッグ" 微速スクロール (ファイルビューワ) "アイコンをクリック" ひとつだけ選択 [Shift]+"アイコンをクリック" ひとつ追加 [Alt]+"アイコンをクリック" 前選択との間にはさまれたものをすべて選択 "アイコンをドラッグ" ファイルを複写、または移動 [Alt]+"アイコンをドラッグ" ファイルを複写 [Command]+"アイコンをドラッグ" ファイルを移動 [Cntrl]+"アイコンをドラッグ" ファイルのシンボリックリンクを作成 [Esc] 現選択を検索パネルへ代入 (検索パネル) [Esc] ファイル名の補完
サービスアプリケーションの作成方法と利用方法
Service提供側の準備: (1) NSAppのデレゲートメソッド -(void)applicationDidFinishLaunching:(NSNotification*) の中で [NSApp setServicesProvider:(id)] でサービスを提供するオブジェクトを登録する。 (2) サービスを提供するメソッドを -(void)サービスを提供するメソッド名:(NSPasteboard*) -(void)サービスを提供するメソッド名:(NSPasteboard*)userData:error: という形式で実装する。 (3) CustomInfo.plistファイルを用意 サービス名、メニューの種類、サービスを提供するメソッド名、表示文字列などを定義する。 用意したCustomInfo.plistはProjectBuilderのOtherResourcesなどに登録する。 (補足) UserDataつきのサービスメッソドを実装する場合、 -(void)サービスを提供するメソッド名:(NSPasteboard*)userData:(NSString*)error:(NSString**) を利用し、エラーを返す場合はerrorにnon-nilのNSString*を与える。 サービスを提供するメソッドの例: -(void)receiveRequest:(NSPasteboard*)pb userData:(NSString*)dat error:(NSString**)err { NSArray *types; NSString *pbString; types=[pb types]; if( ! [types containsObject:NSStringPboardType]) return; pbString=[pb stringForType:NSStringPboardType]; if( ! pbString) return; .... } 標準的なメニュー定義の例: NSServices = ( { NSMessage = messageName; NSPortName = programName; NSSendTypes = ( type1 [, type2] ... ); NSReturnTypes = ( type1 [, type2] ... ); NSMenuItem = { default = item; [ language = item; ] }; NSKeyEquivalent = { default = character; [ language = character; ] }; NSUserData = string; NSTimeout = milliseconds; NSHost = hostName; NSExecutable = pathname; } [, { another service entry } ] ... ); NSMessage サービスを提供するメソッド名 NSPortName [NSApp setServiceProvider:]でサービスを登録した場合、アプリケーション名 NSRegisterServicesProvider()でサービスを登録した場合、この関数に与えた引数 NSSendTypes サービスの要求側から送られるNSPasteboardの型の配列 NSReturnTypes サービスの要求側に返されるNSPasteboardの型の配列 NSMenuItem メニューに表示する文字列、言語毎に設定可能 階層メニューを登録する場合は"Mail/Send"のように"/"で区切って設定する NSKeyEquivalent メニューに対応するショートカットキー、言語毎に設定可能 NSUserData サービス提供側でこのデータを拾い、処理する 設定値はサービス提供側で自由に設定できる NSTimeout サービス提供側からの反応待ち時間、ミリ秒単位で指定、デフォルトは3000(3秒) NSHost リモートのサービスを利用する場合、ホスト名を指定 通常はローカルで提供されているサービスを利用 NSExecutable サービスを提供するアプリケーションのパスを指定 フィルターサービスなどに有効 Service利用側の準備: (1) NSAppのデレゲートメソッド -(void)applicationDidFinishLaunching:(NSNotification*) の中で [NSApp registerServicesMenuSendTypes:(NSArray*) returnTypes:(NSArray*)] を呼びだし、 自分から送りだすNSPasteboardの型の配列と 利用するサービスから受け取ることができるNSPasteboardの型の配列を登録する。 これにより、アプリケーションメニューにサービスメニューが追加され、 サービスが利用可能になる。 逆にこのメソッドを呼びださないと、メニューにサービスメニューは現れない。 (2) -(id)validRequestorForSendType:(NSString*) returnType:(NSString*) にてサービス利用側が授受できるデータタイプの検査ロジックを実装 (3) -(BOOL)writeSelectionToPasteboard:(NSPasteboard*)types:(NSArray*) にてサービス提供側に送るデータを用意する。 (4) サービス提供側が結果を返す場合、 -(BOOL)readSelectionFromPasteboard:(NSPasteboard*)types:(NSArray*) にてサービス提供側から返された結果を受け取る。 Serviceのテスト: サービスを提供するアプリケーションのサービス機能を確認する手順は次のとおり。 (1)アプリを再ビルド(リンクフェーズを含むこと) (2)ビルドしたアプリを実行 (3)ログアウト後、再ログイン (4)再度アプリを実行 この手順に従わないと他アプリケーションのサービスメニューに 提供したサービスのメニューが追加されない場合がある。 Serviceの強制実行: プログラムの中で強制的にサービスを利用する場合、 BOOL NSPerformService(NSString*,NSPasteboard*)を呼ぶ。 Add-on Serviceの利用方法: Add-on Service はダイナミックに処理内容が変わるサービス。 この場合はCustonInfo.plistのメニュー定義のuserDataを利用すると便利。 例えばシェルコマンドの実行など。 複数のシェルコマンドをuserDataに埋め込み、それぞれのメニューは同一のメソッドを呼びだせばよい。 Add-on ServiceはInfo.plistを含む.serviceの拡張子をもつバンドルとして作成する。 このバンドルは ~/Library,/LocalLibrary,/NextLibaryのいずれかのServicesディレクトリに格納すること。 サービスの登録状況を強制的に更新するには、UNIXコマンド make_servicesを使う。 アプリケーションが実行時にサービスを生成し、それを即時に利用可能にする場合は、 NSUpsateDynamicServices(void)を呼ぶこと。 その他のサービス: フィルター、プリントフィルター、スペルチェッカーがある。 それぞれ、Info.plistの記述が多少異なる。 要チェック。
パッケージの作り方
つぎのコマンドを使ってパッケージを作る。 > /NextAdmin/Installer.app/package [ルートディレクトリ] [infoファイル名] -d [格納ディレクトリ] パッケージを作る際にはつぎのような書式でinfoファイルを必ず用意する。 書式: 各行について、"キー 空白 値"の組でパラメータを指定 指定できるキーはつぎのとおり。 Title パッケージの名前 Version バージョン番号 Description パッケージを開いたときに表示されるメッセージ DefaultLocation 標準のインストール先 Relocatable BOOL値で指定 DiskName FDにつけられる名前 ex).APP%d Application BOOL値で指定 DeleteWarning 削除するときに画面に表示されるメッセージ
アイコンビルダにて透明色で描画
アイコンビルダにて透明色で描画 (1)メニュー、ドキュメント>新規配置でつぎのように設定 サイズ 48 x 48 背景カラー 特に指定なし ビット階調 12bitカラー & アルファ値つき (2)カラーパネルでつぎのように設定 不透明点 0 オーバーレイモードペイント OFF ※ アルファ値とは アルファ値とは色の透過度をあらわす値。 0で透過し、1で透過しない。
NSBundleの作成方法
1. ProjectBuilderでプロジェクト型LodableBundleで新規プロジェクト作成。 2. プロジェクトインスペクタのプロジェクト型で主クラスを指定。 注意: 2で主クラスを指定しないと-principalClassで主クラスをインスタンス化できない。 注意: Bundle内で独自に定義したクラスは-principalClass後にすべてインスタンス化できるようになる。 注意: 同一Bundleの再読み込みはエラーにならない。 注意: 異なるBundleで同一のクラス名(主クラス名を含む)を用いて その両バンドルを読み込むと、 -principalClassの呼び出し時にエラーが発生し、 アプリケーションは異常終了。 このエラーを防ぐにはNSBundleDidLoadNotificationの通知を利用する。 -principalClassの呼び出し後にNSBundleDidLoadNotificationが通知される。 通知を受けるメソッド内では、 受け取ったNSNotification内のUserInfoで 次のようなロードしたクラスの配列を受け取る。 {NSLoadedClasses=(クラス名,クラス名,...,クラス名);} 同一Bundleを再読み込みしたときはこの通知は発生しない。 異なるBundleを読み込むときにクラス名が重複した場合、 この通知も発生せずアプリケーションは異常終了。
動的なライブラリと静的なライブラリ
libtoolコマンドによって -staticオプションで静的なライブラリを、 -dynamicオプションで動的なライブラリを作成できる。 < 動的なライブラリにはふたつの制限がある。 (1)ふたつ以上のオブジェクトにシンボルは定義されない。 (2)共通のシンボルも定義されない。 動的なライブラリを作成するにはld で -dylib を付加する。 -staticオプションで作成した静的なライブラリは ranlibと互換性をもつ。 アーカイブされたファイル一覧は アーカイブの中の__.SYMDEFに格納されている。 (注意) コンバイラオプションの -dynamic/-staticでも生成されるオブジェクトファイルが変わる。 -dynamicでコンパイルしたものは-staticでリンクできない。 その逆も不可。当然のこと? (疑問1) 静的なライブラリを動的なライブラリに変更することは可能だが、 その逆は不可能なのでは? 動的なライブラリにはシンボルテーブルが含まれないため、 アーカイブから元のオブジェクトファイルを復元できない? libtoolのマニュアルにも動的ライブラリ用のオプションで オブジェクトファイル復元に関するものは見当たらなかった。 (疑問2) 一度lipoコマンドによってFAT形式にされたライブラリは 元の形を復元することが可能であろうか。 -extract だけではひとつのアーキテクチャのライブラリのみが格納されたFATライブラリが出力される。したがって ar で格納ファイルを復元できない。 -extract_family sparc だと ar で一覧表示可能な形式でファイルが出力されるが、 ファイル名にごみが含まれているみたい。 (疑問3) NEXTSTEPとOPENSTEPで 標準添付されるstaticライブラリに過不足はあるのか? 互換性はあるのか?
静的なパレットと動的なパレットの違い
InterfaceBuilderなどで使用する OPENSTEPにおけるソフトウェア部品のパレットには 静的なパレットと動的なパレットの2種類がある。 これらの違いは次のとおり。 静的なパレット: プロジェクトとして生成、コードをもつ コンパイル必要 インスペクタとエディタを持てる 動的なパレット: 特別なコードを持たない、標準パレットのユニークな設定として作られることが多い コンパイル不要 インスペクタとエディタを持てない
FATバイナリの作成方法
たとえば、Intelマシン、SPARCワークステーション、NeXTワークステーションの 3機種で実行可能なバイナリを作成する場合は、 コンパイルとリンカのオプションに -arch i386 -arch sparc -arch m68k を追加する。 既にあるバイナリがFATバイナリか否かを判断するには、 > lipo -info [ファイル名] とすればよい。
静的なリンクに関連するldのオプション
-prebind 実行ファイルあるいはダイナミックシェアライブラリの未定義シンボルを ダイナミックライブラリのアドレスへ前もってバインドする。 ライブラリがオーバーラップせず、上書きされるシンボルもない場合に このオプションは有効。 -ObjC Objective C のクラスあるいはカテゴリが格納されたスタティックライブラリを すべてロードする。 -all_load すべてのスタティックライブラリをロードする。 -keep_private_externs プライベートな外部シンボルをスタティックシンボルに変更しない (疑問) 他アーキテクチャのソースを移植してコンパイルする場合に 静的なリンクは重要になるのかな?
ProjectBuilderからmakeに渡される引数の例
例えば、 プロジェクト型toolのプログラムを sparc、m68k、i386の3アーキテクチャでビルドした場合の 引数はつぎのとおり。 引数1 tool 引数2 TARGET_ARCHS=sparc m68k i386 引数3 RC_ARCHS=sparc m68k i386 引数4 RC_CFLAGS=-arch sparc -arch m68k -arch i386 引数5 RC_sparc=YES 引数6 RC_m68k=YES 引数7 RC_i386=YES 引数8 -f Makefile
日本語環境で作成したアプリを英語環境でも実行できるようにするには
日本語のリソースを含むたアプリを 日本語フォントを持たない英語環境で実行すると当然文字化けを起こす。 このようなことにならないように、 日本語リソースに加え、英語リソースもアプリに同梱すればよい。 その方法はつぎのとおり。 (1) プログラム中に埋め込まれた日本語文字は、 外部ファイルから読み込んでから使うように変更する。 (2) 日本語を含むリソースをすべて多言語対応ファイルにする。 - ProjectBuilderのボタンから、 プロジェクトインスペクタ>ファイル属性を選び、 ファイル属性の多言語対応可能リソースをチェックする。 ちなみに、nibファイルは標準で多言語対応する。 (3) 日本語リソースをコピーする。 - プロジェクトのディレクトリ内に 手作業でEnglish.lprojディレクトリを新規作成する。 - 作成したディレクトリに Japanese.lprojの内容をすべてEnglish.lprojの中へコピーする。 (4) English.lproj内のリソースを InterfaceBuilderやテキストエディタなどで英語に直す。 英語環境での動作確認はPreference.app にて、 各言語対応プレファレンス>言語を日本語からEnglishに変更してから アプリケーションを実行すればよい。 (注意) ProjectBuilder のプロジェクト属性の言語を Japanese->Englishに変えても Japanese.lprojがEnglish.lprojにリネームされるだけなので、 日本語アプリを英語にも対応させるには、 手作業でEnglish.lprojディレクトリを新規作成し、 Japanese.lprojの内容をすべてEnglish.lprojの中へコピーする。 その後はEnglish.lprojの中のファイルを手作業で英語対応にする。
OPENSTEPのプロジェクトをXServerで使う
OPENSTEPで作成したプロジェクトをMacOSXServerで使うにはPB.projecctとMakefileの一部を修正する。 PB.project、MakfefileともにMAKEFILEDIRをつぎのように変更する。
PB.projecct: MAKEFILEDIR="/System/Developer/Makefiles/pb_makefiles"; Makefile: MAKEFILEDIR=/System/Developer/Makefiles/pb_makefiles
ユーザーデフォルトの種類
NSUserDefaultsで取得できるデフォルト値にはつぎの種類がある。 ドメイン 指定する名前 ドメインの種類 ----------------+----------------------+---------------- 引数 NSArgumentDomain 一時 アプリケーション アプリケーション名 永続 グローバル NSGlobalDomain 永続 言語 言語名 一時 登録 NSRegistrationDomain 一時 ここで挙げた名前でドメインに格納されている デフォルト値の辞書を取得し、 その辞書から実際の値を取得する。
文字コード変換
OPENSTEP内部で文字はすべてUNICODEで扱われる。 他文字コードはすべてUNICODEに変換して文字列となる。 したがって、 他文字コードのままではNSStringとして扱うことはできないため、 他文字コードはNSDataとしてバイナリデータとして扱い、 文字として処理する場合はNSStringに変換する。 ・NSData → NSString (他文字コードを文字列に変換) NSString -initWithData:(NSData*) encoding:(NSStringEncoding) ・NSString → NSData (文字列を任意の文字コードに変換) NSString -(NSData*)dataUsingEncoding:(NSStringEncoding) ・標準エンコーディングの取得 NSString +(NSStringEncoding)defaultCStringEncoding ・エンコーディングの種類 NSASCIIStringEncoding = 1, /* 0..127 only */ NSNEXTSTEPStringEncoding = 2, NSJapaneseEUCStringEncoding = 3, NSUTF8StringEncoding = 4, NSISOLatin1StringEncoding = 5, NSSymbolStringEncoding = 6, NSNonLossyASCIIStringEncoding = 7, /* 7-bit verbose ASCII to represent all unichars */ NSShiftJISStringEncoding = 8, NSISOLatin2StringEncoding = 9, NSUnicodeStringEncoding = 10,
NSArchiverの使い方
オブジェクトが自分自身を保存/復元するサンプル @implementation foo -init { [super init]; var=1; ivar=[[Foo foo] retain]; return self; } -(void)dealloc { [ivar release]; [super dealloc]; } -(BOOL)encodeToFile:(NSString*)path <- オブジェクトの保存 { return [NSArchiver archiveRootObject:self toFile:path]; あるいは NSMutableData *data=[NSMutableData data]; NSArchiver *archiver= [[NSArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:self]; return [data writeToFile:path atomically:YES]; } -initWithFile:(NSString*)path <- オブジェクトの復元 { return [[NSUnarchiver unarchiveObjectWithFile:path] retain]; あるいは NSData *data=[NSData dataWithContentsOfFile:path]; NSUnarchiver *unarc= [[NSUnarchiver alloc] initForReadingWithData:data]; return [[unarc decodeObject] retain]; } -(void)encodeWithCoder:(NSCoder*)coder <- Archiverから呼ばれる { [super encodeWithCoder:coder]; <-(a) [coder encodeValueOfObjCType:@encode(foo) at:&var]; [coder encodeObject:ivar]; } -initWithCoder:(NSCoder*)decoder <- Unarchiverから呼ばれる { [super initWithCoder:decoder]; <-(a) [decoder decodeValueOfObjCType:@encode(foo) at:&var]; ivar=[[decoder decodeObject] retain]; return self; } @end (A)は上位カスタムクラスで すでにオブジェクトの保存/復元用メソッドが実装されている場合のみ必要。 NSObjectから直接のサブクラスでは不要。 NSObjectでは-encodeWithCoderは実装されていないため。
NSArchiverクラスとNSSerializerクラスの違い
NSArchiverとNSSerializerのどちらもオブジェクトの永続的な保存に関連するクラスだが、次のような違いがある。 NSArchiver: NSCoderの具体クラス。 オブジェクトをファイルに書き込むことができる書式(NSMutableData)にエンコード。 クラス情報とインスタンス変数を保存。 NSSerializer: プロパティリストをNSDataのオブジェクトとして保存。 クラス情報は保存されず、構造情報のみ保存される。 またMutableなクラスの変更可能性は保証されない。 復元時に静的なクラスになってしまう。 (注)プロパティリスト: NSDictionary,NSArray,NSString,NSDataで構成されるリスト
NSBundleの説明
NSBundelとは、 Bundle=実行コード+それが利用する資源、 資源=イメージ、サウンド、ローカライズ用の文字列、nibファイルなど。 (Bundleの型) >Application 拡張子.appのディレクトリに格納。メインバンドルとも呼ぶ。 最低でもmain()とアプリケーションの起動に必要なコードを含む。 アプリケーションも要はバンドルの1種。 >Framework 拡張子.frameworkのディレクトリに格納。要は共有ライブラリ+関連資源 関連資源=nibファイル、イメージ、サウンド、ヘッダ、ドキュメントなど。 >Loadable Bundle 拡張子.bundleのディレクトリに格納(他の拡張子も可)。 アプリケーションが動的に読み込むことができるバンドル。 >Palette InterfaceBuilder用に特殊化されたLoadableBundle。 UI+実行コード (bundlePathとresourcePathの違い) 例えばアプリケーションパスが"MyApp.app"の場合、 [[NSBundle mainBundle] bundlePath] → "MyApp.app" [[NSBundle mainBundle] resourcePath] → "MyApp.app/Resources" を返す。 さらに、-pathForResource:...系のメソッドを使うと、 ローカライズディレクトリなど複数ディレクトリを検索して パスを取得できる。 (bundleの作成) サブプロジェクトでバンドルを作成する場合、 サブプロジェクトのインスペクタ画面でprincipalクラスなどの設定が可能。
オブジェクトの自動解放で注意すること
メソッドのスコープ内で生成したオブジェクトは スコープから抜ける際、自動的に解放される。 つまりは 自動的に release メッソドが発行される。 ex.) - foo { NSString *s=[NSString stringWithString:@"Test"]; [s release] ← これは不要 メソッド終了時に自動的にreleaseされるので } 逆に、メソッドの中で生成したオブジェクトを メソッドの外でもこのオブジェクトを有効にするには、 retain メソッドを発行する。 この場合、生成した親オブジェクトは そのオブジェクトのreleaseに責任をもつこと。 おまけ: +alloc & -init & -autorelase = +[classname] ex.) [[NSString alloc] init] autorelase] = [NSString string]
NSCharacterSetとは
NSCharacterSetは文字の検査に利用できる。 NSStringの-rangeOfCharacterFromSet:を用いると、 与えた文字列の中に含まれる文字が NSCharacterSetに登録された文字の集合にあるか確認できる。 NSCharacterSetには、 標準の文字集合を与えることもできるし、 与えた文字集合の補集合も容易に求めることができる。 また、任意の文字集合も設定できる。
NSCellとNSFormatter
-setFormatter:によってNSCellのインスタンスにNSFormatterのインスタンスを割り当てることで、入力値の検査などが可能になる。 実際の入力値の検査はNSFormatterのサブクラスなどで -isPartialStringValid:newEditingString:errorDescription: -stringForObjectValue: -getObjectValue:forString: をオーバーライドすることで実現する。 -isPartialStringValid:...は、キーが押される度に入力値を検査する場合に用いる。 -stringForObjectValue:では、オブジェクトを文字列に変換する。 -getObjectValue:...では、文字列をセルに割り当てられたオブジェクトに変換する。
NSBrowerクラスの説明
NSBrowserクラスはAppKitのクラスうちのひとつであり、 階層型のデータを表示する際(ファイルの階層表示など)に使う。 表示するデータはNSBrowserのdelegateが用意する。 Browserは アクティブとパッシブブの2種類が用意されている。 アクティブではユーザーがカラム内の行を作成することが可能。 パッシブはいくつの行を作成するか通知するのみ。 ただし行のカスタマイズは不可。NSBrowserCellを使用。 カスタマイズ不要ならパッシブで十分。 必須のdelegateメソッドはつぎのとおり。 -(void)browser:(NSBrowser*)willDisplayCell:(NSCell*)atRow:(int)column:(int) 与えられたセルに値やステータスを与える。 このメソッドにより各セルの値を設定する。 -(void)browser:(NSBrowser*)createRowsForColumn:(int)inMatrix:(NSMatrix*) 与えられたMatrixにCellを加える(アクティブで必須)。 -browser:numberOfRowsInColumns: 与えられた番号に相当するカラムに表示する行数を返す(パッシブで必須)。
画像の合成(Postscriptの演算モード)
画像の合成にはPostscriptの演算モードを指定する 次に画像A(デスティネーション)に画像B(ソース)を合成すると仮定して説明する (背景部とは背景色をもつ部分、前景部とは背景色以外の部分を指す)。 COPY 画像Aを画像Bで置き換える CLEAR 画像Aを背景色で塗りつぶす SOVER 画像Bの前景部を画像A全体に重ねる SIN 画像Bの全体を画像Aの前景部に重ねる SOUT 画像Bの全体を画像Aの背景部に重ねる SATOP 画像Bの前景部を画像Aの前景部に重ねる DOVER 画像Aの前景部を画像B全体に重ねた画像に画像Aを置き換える DIN 画像Aの全体を画像Bの前景部に重ねた画像に画像Aを置き換える DOUT 画像Aの全体を画像Bの背景部に重ねた画像に画像Aを置き換える DATOP 画像Aの前景部を画像Bの前景部に重ねた画像に画像Aを置き換える XOR 画像Aと画像Bの前景部が重なる部分を背景色で塗りつぶす PlusD 前景部どうし、色の3原色の小さい値優先で置き換える PlusL 前景部どうし、色の3原色の大きい値優先で置き換える
画像処理に関連するクラス
NSImageView フレーム内にNSImageを表示するためのView 拡大縮小表示、アライメント、外枠設定も処理できる。 NSImage さまざまなデータフォーマットに対応する画像データの抽象クラス。 NSImageRep 画像データから実際の表現(NSImage)を生成する。 NSBitmapRep BMPファイル、TIFFファイルからNSImageを生成。 NSEPSImageRep EPSファイルからNSImageを生成。 NSCustomImageRep アプリケーション内で定義されている再描画メソッドを使いNSImageを生成 アプリケーションで独自に管理する画像。 NSCachedImageRep すでに使用しない画像や画面外に表示されている画像などの再生成するために レンダリング済の画像を一時保管する。通常は使わないらしい。
カスタムビューでイベントを受け取るには?
カスタムビューでマウスなどのイベントを受け取るには (カスタムビューをレスポンダとして振る舞わせるには) -(BOOL)acceptsFirstResponder { return YES; } をカスタムビュー内に用意し、 -acceptsFirstResponderをオーバーライドすること。
インスタンス描画の例
インスタンス描画は一時的な描画に使う。 ウィンドウを再描画すると消えてしまう。 例えばウィンドウを動かしたり、閉じたり、 -setNeedsDisplayなどを呼び出したりすると消える。 (例) @implemantation MyView -(BOOL)acceptsFirstResponder { return YES; } -(void)mouseDown:(NSEvent*)ev { float w=10.; NSPoint winPos=[ev locationInWindow]; NSPoint locPos=[self convertPoint:winPos fromView:nil]; [self lockFocus]; PSnewinstance(); PSsetinstance( YES); ここに描画をコード挿入 PSsetinstance( NO); [self unlockFocus]; } @end
ウィンドウサーバとウィンドウ番号
[ウィンドウ番号] 画面に表示されている各ウィンドウ(NSWindowのインスタンス)には ウィンドウ番号が割り当てられている。 [NSWindows windowNumber]で取得するウィンドウ番号はローカルなもので、 ウィンドウサーバが管理しているグローバルなウィンドウ番号ではない。 [ウィンドウの順番変更] [NSWindow orderWindow:(NSWindowOrderingMode)place relativeTo:(int)otherWNum] このメソッドはウィンドウサーバの画面リスト内の自分のウィンドウの 位置を変更するもの。 placeで NSWindowOutを指定すると画面リストから除去 NSWindowAboveを指定するとotherWNumの直前に移動 NSWindowBelowを指定するとotherWNumの直後に移動 otherWNumに0を指定すると画面リストの最前、あるいは最後に移動 [ウィンドウ関連関数] (void)NSConvertWindowNumberToGlobal( int wnum, int *gwnum); (void)NSConvertGlobalToWindowNumber( int gwnum, int *wnum); (void)NSCountWindows( int *count); (void)NSWindowList( int size, int list[]); [ウィンドウサーバとの通信の切替] (例)印刷時の通信先の切替 [NSDPSContext setCurrentContext:[NSApp context]]; /* Communicate with the Window Server. */ /* スクリーン表示が可能に */ [NSDPSContext setCurrentContext:[[NSPrintOperation currentOperation] context]]; /* Resume generating PostScript code. */ /* 印刷へ接続を戻す */
描画ではlockFocusが必要
PS関数で描画する場合、 描画コードをlockFocusとunlockFocusで囲まなければならない。 lockFocusを呼び出さないと描画の準備が整わないため、 いくらPS関数を実行しても無視される (drawRectなど一部メソッドではlockFocusは不要)。 この描画がすぐに画像に反映されるか否かは NSWindowのBackingの指定による。 non-retainedならすぐに表示されるが、 他ウィンドウを重ねると重なった部分の画像が消えてしまう。 bufferedとretainedはすぐに表示されず、 flushWindowを呼び出すことで表示される。 ただしこの場合、 ウィンドウのオープン、クローズで描画の内容は消えてしまう。 一度描画したら内容を変更しないような場合は、 drawRectにて描画すればよいのでlockFocusは不要。 マウスイベントなどによって描画内容を変化される場合は、 NSImageをバッファ代わりに利用する。 描画は常にNSImageに対して行い、 drawRectの中でNSImageの内容を表示させたいViewへコピーする。
描画で使うデータ型と関数
[定義] typedef struct _NSPoint { /* 位置を表す */ float x; float y; } NSPoint; typedef struct _NSSize { /* 大きさを表す */ float width; /* should never be negative */ float height; /* should never be negative */ } NSSize; typedef struct _NSRect { /* 領域を表す */ NSPoint origin; NSSize size; } NSRect; typedef enum _NSRectEdge { NSMinXEdge = 0, NSMinYEdge = 1, NSMaxXEdge = 2, NSMaxYEdge = 3 } NSRectEdge; [定数] const NSPoint NSZeroPoint; const NSSize NSZeroSize; const NSRect NSZeroRect; [関数/生成] NSPoint NSMakePoint(float x, float y) NSSize NSMakeSize(float w, float h) NSRect NSMakeRect(float x, float y, float w, float h) [関数/Rectを調査] float NSMaxX(NSRect aRect) float NSMaxY(NSRect aRect) float NSMidX(NSRect aRect) float NSMidY(NSRect aRect) float NSMinX(NSRect aRect) float NSMinY(NSRect aRect) float NSWidth(NSRect aRect) float NSHeight(NSRect aRect) [関数/比較] BOOL NSEqualPoints(NSPoint aPoint, NSPoint bPoint); BOOL NSEqualSizes(NSSize aSize, NSSize bSize); BOOL NSEqualRects(NSRect aRect, NSRect bRect); BOOL NSIsEmptyRect(NSRect aRect); [関数/Rectの演算] NSRect NSInsetRect(NSRect aRect, float dX, float dY); NSRect NSIntegralRect(NSRect aRect); NSRect NSUnionRect(NSRect aRect, NSRect bRect); NSRect NSIntersectionRect(NSRect aRect, NSRect bRect); NSRect NSOffsetRect(NSRect aRect, float dX, float dY); void NSDivideRect(NSRect inRect, NSRect *slice, NSRect *rem, float amount, NSRectEdge edge); BOOL NSPointInRect(NSPoint aPoint, NSRect aRect); BOOL NSMouseInRect(NSPoint aPoint, NSRect aRect, BOOL flipped); BOOL NSContainsRect(NSRect aRect, NSRect bRect); BOOL NSIntersectsRect(NSRect aRect, NSRect bRect); [関数/表示用文字列へ変換] NSString *NSStringFromPoint(NSPoint aPoint); NSString *NSStringFromSize(NSSize aSize); NSString *NSStringFromRect(NSRect aRect); NSPoint NSPointFromString(NSString *aString); NSSize NSSizeFromString(NSString *aString); NSRect NSRectFromString(NSString *aString);