[ ホーム | お知らせ | ソフトウェア | 覚え書き | メール | ->英語 ]

テンでテンてこまい

- Cocoaプログラミング -

テンプレート

オブジェクトの初期化と解放

アプリケーションの起動と終了

Nibファイルの読み込み

ウィンドウのクローズ

NSTableView、データソースのプロトコル

ドキュメントクラス

 

一般

基本データ型と関数

クラス、メソッド、インスタンス変数とその名前

インスタンス変数の保有と更新、参照

オブジェクトの自動解放で注意すること

Machカーネル関数とC-Thread関数

プロトコルにはこんな利点も

メッセージの転送について

 

開発ツール

ディスクイメージ(.dmg)の作り方

静的なパレットと動的なパレットの違い

動的なライブラリと静的なライブラリ

静的なリンクに関連するldのオプション

 

Foundation Framework

ユーザーデフォルトの種類

文字コード変換

NSCharacterSetとは

マルチスレッドに関連したクラス

NSArchiverの使い方

NSArchiverクラスとNSSerializerクラスの違い

NSBundleの説明

 

Application Kit Framework

サービスメニューを提供する、利用する

NSCellとNSFormatter

NSBrowerクラスの説明

画像処理に関連するクラス

カスタムビューでイベントを受け取るには?

描画ではlockFocusが必要

 

[ トップページに戻る ]


オブジェクトの初期化と解放

-(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
{
	// アプリケーション終了直前の処理

}


[目次へ]

nibファイルの読み込み

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;
}
[目次へ]

プロトコルにはこんな利点も

プロトコルで定義しているインターフェースに関しては、 そのプロトコルを採用しているクラスの インターフェース(@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関数

Machカーネル関数
 → 基本的なスレッド制御
C-Thread関数
 → 高度なスレッド制御

C-Thread関数では スレッドのForkとJoin、排他制御と同期処理のしくみが用意されている。

[目次へ]

マルチスレッドに関連したクラス

関連クラス

NSThread
NSConnection
NSRunLoop

NSThreadについて

スレッドの実行を制御できない? 実行中かの判断、実行の中断、再開、強制終了など。

ロックに関連したクラス

複数のスレッドで同一データ、オブジェクトを共用する場合には つぎに示すロック機能を持ったクラスを活用する。

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 サービスを提供するアプリケーションのパスを指定 フィルターサービスなどに有効

 

サービスメニューのローカライズ

次のいずれかの方法で、サービスメニューに表示する文字列を各言語別に用意できる。

  1. Info.plistのNSServices>NSMenuItemに「Japanese="日本語文字列";」のように文字列を記述する
  2. 言語別に用意したServicesMenu.strings(例えば、Japanese.lprojの中)に「"標準のメニューの文字列"="言語別の文字列";」のように記述する

※ 標準のメニューの文字列とは、Info.plistのNSServices>NSMenuItemにて「default=...」で設定した文字列のこと

Service利用側の準備

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*)

Serviceのテスト

サービスを提供するアプリケーションのサービス機能を確認する手順は次のとおり。

  1. アプリを再ビルド(リンクフェーズを含むこと)
  2. )ビルドしたアプリを実行
  3. ログアウト後、再ログイン
  4. 再度アプリを実行

この手順に従わないと他アプリケーションのサービスメニューに提供したサービスのメニューが追加されない場合がある。

Serviceの強制実行

プログラムの中で強制的にサービスを利用する場合、 つぎの関数を呼ぶ。

BOOL NSPerformService(NSString*,NSPasteboard*)

その他のサービス

フィルター、プリントフィルター、スペルチェッカーがある。 それぞれ、サービスメニューの記述が多少異なる。 要チェック。

補足

OS X 10.2では、サービスを提供するアプリケーションを置けるのはアプリケーションフォルダ(/Applications)だけ。

OS X 10.3では、サービスを提供するアプリケーションはどのフォルダに置いても利用できる様子。

また、サービスを提供するアプリケーションを削除すればサービスメニューも消える。

そして、Info.pliistからNSServicesの項目を取り除いてもサービスメニューは消える( OS X 10.3で確認)。

[目次へ]

ディスクイメージ(.dmg)の作り方

(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オプションで動的なライブラリを作成できる。

動的なライブラリにはふたつの制限がある。

  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、Mac OS Xで標準添付されるstaticライブラリに過不足はあるのか?互換性はあるのか?

[目次へ]

静的なパレットと動的なパレットの違い

InterfaceBuilderなどで使用するパレット(部品化されたソフトウェア)には静的なパレットと動的なパレットの2種類がある。これらの違いは次のとおり。

静的なパレット

動的なパレット

[目次へ]

静的なリンクに関連するldのオプション

-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内で定義されている。

[目次へ]

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

NSSerializer

(注) プロパティリスト=NSDictionary,NSArray,NSString,NSDataで構成されるリスト

 

[目次へ]

NSBundleの説明

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クラスなどの設定が可能。

[目次へ]

オブジェクトの自動解放で注意すること

メソッドのスコープ内で生成したオブジェクトは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とは

NSCharacterSetは文字の検査に利用できる。

NSStringの-rangeOfCharacterFromSet:を用いると、 与えた文字列の中に含まれる文字が NSCharacterSetに登録された文字の集合にあるか確認できる。

NSCharacterSetには、 標準の文字集合を与えることもできるし、 与えた文字集合の補集合も容易に求めることができる。 また、任意の文字集合も設定できる。

[目次へ]

NSCellとNSFormatter

つぎのメソッドでNSCellのインスタンスにNSFormatterのインスタンスを割り当てと、入力値を検査できる。

NSCell:
-setFormatter:(NSFormatter*)

実際の入力値の検査はNSFormatterのサブクラスなどでつぎのメソッドをオーバーライドする。

NSFormatter:
-isPartialStringValid:(NSString**) newEditingString:(NSString**) errorDescription:(NSString**)
  → キーが押される度に入力値を検査する
-(NSString*)stringForObjectValue:(id)
  → オブジェクトを文字列に変換する
-(id)getObjectValue:forString:(NSString*)
  → 文字列をセルに割り当てられたオブジェクトに変換する。
[目次へ]

NSBrowerクラスの説明

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が必要

描画する場合、描画コードを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);

[目次へ]


[ ホーム | お知らせ | ソフトウェア | 覚え書き | メール | ->英語 ]

空色そふと

eXTReMe Tracker