[ ホーム | お知らせ | ソフトウェア | 覚え書き | メール | ->英語 ] |
テンプレート |
|
一般 |
|
開発ツール |
|
Foundation Framework |
NSArchiverクラスとNSSerializerクラスの違い
|
Application Kit Framework |
|
[ トップページに戻る ]
-(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 { // ウィンドウクローズ直前の処理 }
//データを参照 - (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; }
プロトコルで定義しているインターフェースに関しては、 そのプロトコルを採用しているクラスの インターフェース(@interface)を記述する必要はない。
→ インターフェースの仕様に変更があった場合 プロトコルを変更するだけでよい(そのプロトコル採用しているクラスのインターフェースは 一斉に変更することになり、コーディングの手間が減る)。
→ ただしインプリメンテーション(@implementation)の定義は すべて書き換える必要がある。
受信したメッセージに対応するメソッドがない場合、つぎのメソッドが呼び出される。
NSObject: -(void)doesRecognizeSelector:(SEL)
このメソッドをカスタムオブジェクトでオーバーライドしている場合は呼び出し終了後、つぎのメソッドが呼び出される。
NSObject: -(void)forwardInvocation:(NSInvocation*)
あるオブジェクト(Class1のインスタンス)が受けたインスタンスメソッドを他のオブジェクト(Class2のインスタンス)にそのまま転送する例をつぎに示す。
@implementation Class1 -init { if( self=[super init]) { otherObj=[[[[ Class2 alloc] init] autorelease] retain]; } return self; } -(void)dealloc { [otherObj release]; [super dealloc]; } -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { return [otherObj methodSignatureForSelector:aSelector]; } -(void)forwardInvocation:(NSInvocation*)anInvocation { [anInvocation invokeWithTarget:otherObj]; } @end
NSObjCRuntime.h:
クラス名→クラス |
Class NSClassFromString(NSString*) 名前に一致したクラスがない場合はゼロが返される。 |
クラス→クラス名 |
NSString* NSStringFromClass(Class) |
メソッド名→メソッド |
SEL NSSelectorFromString(NSString*) 名前に一致したメソッドがない場合はゼロが返される。 |
メソッド→メソッド名 |
NSString* NSStrinfFromSelector(SEL) 名前に一致したメソッドがない場合はゼロが返される。 |
objc.h:
SEL sel_getUid( const char*) メソッド名からすでに登録済みのエントリを取得、NSSelectorFromStringに相当 ただし他クラスのメソッドのエントリも取得可 |
SEL sel_registerName( const char*) 指定したメソッド名のエントリを新規に取得 |
const char* sel_getName( SEL) エントリからメソッド名を取得、NSStringFromSelectorに相当 |
objc-class.h:
Ivar class_getInstanceVariables(Class,char*) Ivar object_getInstanceVariable(id,char*,void*) Ivar object_setInstanceVariable(id,char*,void*)
クラス内で生成したインスタンス変数は基本的に、そのクラス自身に保有と解放の責任がある。
-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) { if( iVar == newIVar) return; [iVar autorelease]; iVar=[newIVar retain]; } -(void)setIVar:(Foo*)newIVar <-(b) { if( iVar == newIVar) return; [iVar autorelease]; iVar=[newIVar copy]; } -(void)setIVar:(Foo*)newIVar <-(c) { if( iVar == newIVar) return; 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関数 → 高度なスレッド制御
C-Thread関数では スレッドのForkとJoin、排他制御と同期処理のしくみが用意されている。
NSThread NSConnection NSRunLoop
スレッドの実行を制御できない? 実行中かの判断、実行の中断、再開、強制終了など。
複数のスレッドで同一データ、オブジェクトを共用する場合には つぎに示すロック機能を持ったクラスを活用する。
NSLock 実行制御の移行をブロックしないロック機能をもつ NSConditionLock 条件が満たされるまで実行制御の移行を延期するロック機能をもつ NSRecursiveLock デッドロックを発生させることなく 単一のスレッドが複数回数ロックを所得できるロック機能をもつ
(1) NSApplicationのデレゲートメソッド、-(void)applicationDidFinishLaunching:(NSNotification*) の中でつぎのメソッドを使い、 サービスを提供するオブジェクトを登録する。
NSApplication: -(void)setServicesProvider:(id)
(2) サービスを提供するメソッドをつぎのどちらかの形式で実装する。
-(void)サービスを提供するメソッド名:(NSPasteboard*) -(void)サービスを提供するメソッド名:(NSPasteboard*) userData:(id) error:(NSString**)
(3) Project Builderを使い、サービス名、メニューの種類、サービスを提供するメソッド名、表示文字列などを定義する。
(補足)
UserDataつきのサービスメッソドを実装する場合、つぎのメソッドを利用し、エラーを返す場合はerrorにnon-nilのNSString*を与える。
-(void)サービスを提供するメソッド名:(NSPasteboard*)userData:(NSString*)error:(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; .... }
ProjectBuilderのTarget>Application Settings>Expertの画面を使い、サービスメニューへ対応させるためにNSServicesを追加する。
Application Settingの内容はInfo.plistに保存される。Application Settingを使いNSServicesを設定すると、Info.plistにはつぎのような内容が記録される。Application Settingを使ったNSServicesの追加作業の参考にするとよい。
<plist version="0.9"> <dict> ... <key>NSServices</key> <array> <dict> <key>NSMenuItem</key> <dict> <key>default</key> <string>メニューに表示する文字列</string> </dict> <key>NSMessage</key> <string>サービスで呼び出されるメソッド</string> <key>NSPortName</key> <string>ポートの名前</string> <key>NSSendTypes</key> <array> <string>NSStringPboardType</string> <string>NSSelectionPboardType</string> <string>NSRTFPboardType</string> </array> <key>NSUserData</key> <string>任意に追加</string> </dict> <dict> ふたつ目のメニュー ... </dict> ... </array> </dict> </plist>
この設定を旧プロパティリストの形式でまとめたものをつぎに示す。
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 | サービスを提供するアプリケーションのパスを指定 フィルターサービスなどに有効 |
次のいずれかの方法で、サービスメニューに表示する文字列を各言語別に用意できる。
※ 標準のメニューの文字列とは、Info.plistのNSServices>NSMenuItemにて「default=...」で設定した文字列のこと
NSAppilcationのデレゲートオブジェクトの中につぎに紹介するメソッドを実装する。
(1) NSApplicationのデレゲートメソッド、 -(void)applicationDidFinishLaunching:(NSNotification*) の中でつぎのメソッドを呼びだし、 自分から送りだすNSPasteboardの型の配列と 利用するサービスから受け取ることができるNSPasteboardの型の配列を登録する。
NSApplication: -(void)registerServicesMenuSendTypes:(NSArray*) returnTypes:(NSArray*)]
これにより、アプリケーションメニューにサービスメニューが追加され、 サービスが利用可能になる。 逆にこのメソッドを呼びださないと、メニューにサービスメニューは現れない。
(2) つぎのメソッドを実装し、サービス利用側が授受できるデータタイプを検査する。
NSApplication: -(id)validRequestorForSendType:(NSString*) returnType:(NSString*)
(3) つぎのメソッドにて、サービス提供側に送るデータを用意する。
NSApplication: -(BOOL)writeSelectionToPasteboard:(NSPasteboard*) types:(NSArray*)
(4) サービス提供側が結果を返す場合、 つぎのメソッドにてサービス提供側から返された結果を受け取る。
NSApplication: -(BOOL)readSelectionFromPasteboard:(NSPasteboard*) types:(NSArray*)
サービスを提供するアプリケーションのサービス機能を確認する手順は次のとおり。
この手順に従わないと他アプリケーションのサービスメニューに提供したサービスのメニューが追加されない場合がある。
プログラムの中で強制的にサービスを利用する場合、 つぎの関数を呼ぶ。
BOOL NSPerformService(NSString*,NSPasteboard*)
フィルター、プリントフィルター、スペルチェッカーがある。 それぞれ、サービスメニューの記述が多少異なる。 要チェック。
OS X 10.2では、サービスを提供するアプリケーションを置けるのはアプリケーションフォルダ(/Applications)だけ。
OS X 10.3では、サービスを提供するアプリケーションはどのフォルダに置いても利用できる様子。
また、サービスを提供するアプリケーションを削除すればサービスメニューも消える。
そして、Info.pliistからNSServicesの項目を取り除いてもサービスメニューは消える( OS X 10.3で確認)。
(1) 空のディスクイメージを用意する。サイズは4MB以上を指定する(初期化できないため)。
$ hdiutil create -megabytes 20 TargetImage.dmg -layout NONE -zeroImage
(2) ディスクイメージをHFS+で初期化する。ここでボリューム名を決める。
$ hdid -nomount TargetImage.dmg $ sudo newfs_hfs -v <volume name> /dev/disk<somenumber> $ hdiutil eject /dev/disk<somenumber>
(3) ディスクイメージをマウントしてその中に必要なファイルをコピーする。コピー終了後はアンマウントする。
(4) ディスクイメージを読み込み専用にする。
$ hdiutil convert -format UDCO TargetImage.dmg -o DownloadableImage.dmg -noext
※ 参考文献: http://www.stepwise.com/Articles/Technical/2001-03-29.01.html
libtoolコマンドによって-staticオプションで静的なライブラリを、-dynamicオプションで動的なライブラリを作成できる。
動的なライブラリにはふたつの制限がある。
動的なライブラリを作成するにはld で -dylib を付加する。
-staticオプションで作成した静的なライブラリはranlibと互換性をもつ。
アーカイブされたファイル一覧はアーカイブの中の__.SYMDEFに格納されている。
(注意)
コンバイラオプションの-dynamic/-staticでも生成されるオブジェクトファイルが変わる。-dynamicでコンパイルしたものは-staticでリンクできない。 その逆も不可。当然のこと?
(疑問1)
静的なライブラリを動的なライブラリに変更することは可能だが、その逆は不可能なのでは?
動的なライブラリにはシンボルテーブルが含まれないため、アーカイブから元のオブジェクトファイルを復元できない?
libtoolのマニュアルにも動的ライブラリ用のオプションでオブジェクトファイル復元に関するものは見当たらなかった。
(疑問2)
一度lipoコマンドによってFAT形式にされたライブラリは元の形を復元することが可能であろうか。
-extract だけではひとつのアーキテクチャのライブラリのみが格納されたFATライブラリが出力される。したがって ar で格納ファイルを復元できない。
-extract_family sparc だと ar で一覧表示可能な形式でファイルが出力されるが、ファイル名にごみが含まれているみたい。
(疑問3)
NEXTSTEPとOPENSTEP、Mac OS Xで標準添付されるstaticライブラリに過不足はあるのか?互換性はあるのか?
InterfaceBuilderなどで使用するパレット(部品化されたソフトウェア)には静的なパレットと動的なパレットの2種類がある。これらの違いは次のとおり。
-prebind
実行ファイルあるいはダイナミックシェアライブラリの未定義シンボルをダイナミックライブラリのアドレスへ前もってバインドする。 ライブラリがオーバーラップせず、上書きされるシンボルもない場合にこのオプションは有効。
-ObjC
Objective C のクラスあるいはカテゴリが格納されたスタティックライブラリをすべてロードする。
-all_load
すべてのスタティックライブラリをロードする。
-keep_private_externs
プライベートな外部シンボルをスタティックシンボルに変更しない
(疑問)
他アーキテクチャのソースを移植してコンパイルする場合に静的なリンクは重要になるのかな?
NSUserDefaultsで取得できるデフォルト値にはつぎの種類がある。
ドメイン | 指定する名前 | ドメインの種類 |
---|---|---|
引数 | NSArgumentDomain |
一時
|
アプリケーション | アプリケーション名 |
永続
|
グローバル | NSGlobalDomain |
永続
|
言語 | 言語名 |
一時
|
登録 | NSRegistrationDomain |
一時
|
ここで挙げた名前でドメインに格納されている デフォルト値の辞書を取得し、その辞書から実際の値を取得する。
Cocoa内部で文字はすべてUNICODEで扱われる。他文字コードはすべてUNICODEに変換して文字列にしなければならない。。
したがって、他文字コードのままではNSStringとして扱うことはできないため、他文字コードはNSDataとしてバイナリデータとして扱い、 文字として処理する場合はNSStringに変換する。
・NSData → NSString (他文字コードを文字列に変換) NSString: -initWithData:(NSData*) encoding:(NSStringEncoding) ・NSString → NSData (文字列を任意の文字コードに変換) NSString: -(NSData*)dataUsingEncoding:(NSStringEncoding) ・標準エンコーディングの取得 NSString: +(NSStringEncoding)defaultCStringEncoding
エンコーディング(NSStringEncoding)の種類はNSString.h内で定義されている。
オブジェクトが自分自身を保存/復元するサンプル
@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のどちらもオブジェクトの永続的な保存に関連するクラスだが、次のような違いがある。
(注) プロパティリスト=NSDictionary,NSArray,NSString,NSDataで構成されるリスト
Bundle → 実行コード+それが利用する資源 資源 → イメージ、サウンド、ローカライズ用の文字列、nibファイルなど。
Bundleの型
Application | 拡張子.appのディレクトリに格納。メインバンドルとも呼ぶ。 最低でもmain()とアプリケーションの起動に必要なコードを含む。 アプリケーションも要はバンドルの1種。 |
Framework |
拡張子.frameworkのディレクトリに格納。要は共有ライブラリ+関連資源 。 関連資源 = nibファイル、イメージ、サウンド、ヘッダ、ドキュメントなど |
Loadable Bundle | 拡張子.bundleのディレクトリに格納(他の拡張子も可)。 アプリケーションが動的に読み込むことができるバンドル。 |
Palette |
InterfaceBuilder用に特殊化されたLoadableBundle。 パレット = UI+実行コード |
例えばアプリケーションパスが"MyApp.app"の場合、つぎのように値を返す。
[[NSBundle mainBundle] bundlePath] → "MyApp.app" [[NSBundle mainBundle] resourcePath] → "MyApp.app/Resources"
さらに、-pathForResource:...系のメソッドを使うと、 ローカライズディレクトリなど複数ディレクトリを検索して パスを取得できる。
サブプロジェクトでバンドルを作成する場合、サブプロジェクトのインスペクタ画面でprincipalクラスなどの設定が可能。
メソッドのスコープ内で生成したオブジェクトはretainしないかぎり、 スコープから抜けた後にいつかはreleaseされてしまう。 しかし必ずしもスコープ内でreleaseしなくてもよい。
ex.) - foo { NSString *s=[NSString stringWithString:@"Test"]; ... [s release] ← これはなくてもよい このメソッド終了後の適当なタイミングで自動的にreleaseされる }
あるメソッドを呼出し、そのメソッドが生成したオブジェクトを自分が保有する場合に利用できる。
ex.) Class1: { -(NSString*)makeMessage { return [NSString stringWithString:@"Hello"]; } Class2: -(void)sampleMethod { NSString *message; ... message=[[anObj makeMessage] retain]; ... } ※ anObj = Class1のインスタンス
おまけ
+alloc & -init & -autorelase = +[classname] ex.) [[NSString alloc] init] autorelase] = [NSString string]
NSCharacterSetは文字の検査に利用できる。
NSStringの-rangeOfCharacterFromSet:を用いると、 与えた文字列の中に含まれる文字が NSCharacterSetに登録された文字の集合にあるか確認できる。
NSCharacterSetには、 標準の文字集合を与えることもできるし、 与えた文字集合の補集合も容易に求めることができる。 また、任意の文字集合も設定できる。
つぎのメソッドでNSCellのインスタンスにNSFormatterのインスタンスを割り当てと、入力値を検査できる。
NSCell: -setFormatter:(NSFormatter*)
実際の入力値の検査はNSFormatterのサブクラスなどでつぎのメソッドをオーバーライドする。
NSFormatter: -isPartialStringValid:(NSString**) newEditingString:(NSString**) errorDescription:(NSString**) → キーが押される度に入力値を検査する -(NSString*)stringForObjectValue:(id) → オブジェクトを文字列に変換する -(id)getObjectValue:forString:(NSString*) → 文字列をセルに割り当てられたオブジェクトに変換する。
NSBrowserクラスはAppKitのクラスうちのひとつであり、階層型のデータを表示する際(ファイルの階層表示など)に使う。表示するデータはNSBrowserのdelegateが用意する。
Browserはアクティブとパッシブブの2種類が用意されている。アクティブではユーザーがカラム内の行を作成することが可能。 パッシブはいくつの行を作成するか通知するのみ。ただし行のカスタマイズは不可。NSBrowserCellを使用。カスタマイズ不要ならパッシブで十分。
必須のdelegateメソッドはつぎのとおり。
NSObject(NSBrowserDelegate): -(void)browser:(NSBrowser*)willDisplayCell:(NSCell*)atRow:(int)column:(int) → 与えられたセルに値やステータスを与える。このメソッドにより各セルの値を設定する。 -(void)browser:(NSBrowser*)createRowsForColumn:(int)inMatrix:(NSMatrix*) → 与えられたMatrixにCellを加える(アクティブで必須)。 -browser:numberOfRowsInColumns: 与えられた番号に相当するカラムに表示する行数を返す(パッシブで必須)。
NSImageView フレーム内にNSImageを表示するためのView 拡大縮小表示、アライメント、外枠設定も処理できる。 NSImage さまざまなデータフォーマットに対応する画像データの抽象クラス。 NSImageRep 画像データから実際の表現(NSImage)を生成する。 NSBitmapRep BMPファイル、TIFFファイルからNSImageを生成。 NSEPSImageRep EPSファイルからNSImageを生成。 NSCustomImageRep アプリケーション内で定義されている再描画メソッドを使いNSImageを生成 アプリケーションで独自に管理する画像。 NSCachedImageRep すでに使用しない画像や画面外に表示されている画像などの再生成するために レンダリング済の画像を一時保管する。通常は使わないらしい。
カスタムビューでマウスなどのイベントを受け取るには(カスタムビューをレスポンダとして振る舞わせるには)、つぎのメソッドをカスタムビュ-内に用意する。
-(BOOL)acceptsFirstResponder { return YES; }
描画する場合、描画コードを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);