kdoc - Cocoa2

  • 作成日:2007-01-17 16:03:34
  • 修正日:2008-06-05 17:45:28

このページは…

↑ページトップへ

『Mac OS X Cocoaプログラミング / アーロン・ヒレガス』Objective-CのこともCocoaのこともさっぱり分からないながら、『Mac OS X Cocoaプログラミング / アーロン・ヒレガス』を読みながらなんとか理解してみようと試みながらのメモ。

はじめに

↑ページトップへ

  • クラス名は頭大文字…Class
  • メソッド名は小文字…method
  • インスタンス変数名も小文字
  • NS…NeXTSTEP
  • nib…NeXT Interface Builder
  • IBOutlet…nothingへと評価されるマクロ。無視。Interface Builderが内部の構文解析で使用するもの。
  • IBAction…voidと同じ。Interface Builderが内部で使用するもの。
  • nil…NULL。オブジェクトへのポインタに対してはNULLではなく、nilを使用する。
    • nilへのメッセージ送信はエラーとならない。
  • Objective-C固有のキーワードは、多くが@で開始する。
    @end、@implementation、@class、@encode…
  • バックスラッシュはoptionキー+¥で入力

ヘッダファイル(.h)

#import <Cocoa/Cocoa.h>
@interface Foo : NSObject
{
    IBOutlet id textField;
}
- (IBAction)generate:(id)sender;
- (IBAction)seed:(id)sender;
@end

インプリメンテーションファイル(.m)

#import "Foo.h"
@implementation Foo
- (IBAction)generate:(id)sender
{
}
- (IBAction)seed:(id)sender
{
}
@end

MainMenu.nib

First Rsponder

  • 便宜的な架空のオブジェクト。

File's Owner

  • 自分自身に相当するNSApplicationオブジェクト
  • イベントキュー(ユーザ操作等のイベントを管理している待ち行列)からイベントを受け取り、適切なウィンドウに転送する役割を持つ。

クラス

  • NSObject…Objective-Cのクラス階層全体の頂点に位置するルートクラス。
  • すべてのクラスは、ヘッダファイルとインプリメンテーションファイルの2つのファイルによって定義される。
  • ヘッダファイルはインターフェースファイルとも呼ばれ、クラスが保持するインスタンス変数とメソッドを宣言するもの。

outletとaction

  • outlet…他のオブジェクトによって起動される変数のこと。
  • action…ユーザインターフェースオブジェクトによって起動されるメソッドのこと。

流れ

  1. 何かのサブクラス(Foo)を作る。
  2. Create Files For Fooで、Foo.hとFoo.mができる。
  3. Instantiate Fooで、インスタンス化し、これとあれこれを接続。

接続

※この矢印の方向に、(コントロール+ドラッグで)接続する。

ウィンドウ【2を足すボタン】【2をかけるボタン】

target/action(2woTasu、2woKakeru)
【Fooのインスタンス】
outlet(kotae)

ウィンドウ【答え表示欄】

Objective-CとJavaと日本語

Java

public void increment (Object sender){
 count ++;
 textField.setIntValue(count);
}

日本語

incrementは、引数としてオブジェクトを1つ受け取るpublicなインスタンスメソッドである。
このメソッドは何も値を返さない。
このメソッドは、インスタンス変数countの値を1だけ増やし、textFieldオブジェクトに対してそのcountを引数としたsetIntValue()メッセージを送信する。

Objective-C

-(void) increment:(id)sender
{
 count ++;
 [textField setIntValue:count];
}

初期化

awakeFromNibを使用。
インプリメンテーションファイル(.m)ファイル中に書く。

- (void) awakeFromNib
{
 NSCalendarDate *now;
 now = [NSCalendarDate calendarDate];
 [textField setObjectValue:now];
}

Objective-C

↑ページトップへ

変数の宣言はブロックの先頭で行なう。

{
 int i = 6;
}

NSMutableArrayクラスの新たなインスタンスを生成

[NSMutableArray alloc]

これでポインタが返る。
ポインタを変数に保持するには、

NSMutableArray *foo;
foo = [NSMutableArray alloc];

fooは単なるポインタ。
fooが指しているオブジェクトを使用するには、まずそのオブジェクトを初期化する必要がある。

NSMutableArray *foo;
foo = [NSMutableArray alloc];
[foo init];

ネストも可能。

NSMutableArray *foo;
foo = [[NSMutableArray alloc] init];

オブジェクトの破棄は以下。

[foo release];

メソッドが引数を受け取る場合は、

[foo addObject:bar];

引数が複数ある場合は、

[foo insertObject:bar atIndex:5];

NSString

@と二重引用符を使用する。

NSString *bar;
bar = @"これはNSStringです。";

メモリ

下記ではresultがリリースされないのでメモリリーク発生。

-(NSString *)description
{
 NSString *result = [[NSString alloc] initWithFormat:@"%@ = %d and %d", entryDate, firstNumber, secondNumber];
 return result;
}

下記ではreturn前にリリースしてしまっていて全然ダメ。

-(NSString *)description
{
 NSString *result = [[NSString alloc] initWithFormat:@"%@ = %d and %d", entryDate, firstNumber, secondNumber];
 [result release];
 return result;
}

この場合、autoreleaseを使う。

-(NSString *)description
{
 NSString *result = [[NSString alloc] initWithFormat:@"%@ = %d and %d", entryDate, firstNumber, secondNumber];
 [result autorelease];
 return result;
}

NSStringには、autoreleaseしてくれるメソッドがある(stringWithFormat)。

-(NSString *)description
{
 return [NSString stringWithFormat:@"%@ = %d and %d", entryDate, firstNumber, secondNumber];
}

releaseされ、保持カウントがゼロになると、dealloc(メモリ割当解放)が呼ばれる(destroy)。

NSControl

↑ページトップへ

上位階層から…

  • NSObject…頂点。retain、release、dealloc、initなど。
  • NSResponder…mouseDonw:、keyDownなど。
  • NSView…ウィンドウ内で自身が描画する領域を管理。
  • NSControl…NSViewを継承し、targetとactionを追加したもの。
  • NSButton、NSSlider、NSTextField…

NSButton

  • (void)setEnabled:(BOOL)yn
  • (BOOL)state
  • (void)setState:(BOOL)yn

NSSlider

  • (void)setFloatValue:(float)x…スライダをxまで移動
  • (void)floatValue…スライダの現在値

NSTextField

1行分のテキスト。

文字列

  • (NSString *)stringValue
  • (void)setStringValue:(NSString *)aString

任意のオブジェクト

例えば、文字列ではなくNSCalendarDateインスタンスをセットしたり返したり。

  • (NSObject *)objectValue
  • (void)setObjectValue:(NSObject *)anObject

NSTableView

NSTableViewは、dataSourse(下記3つのメソッドを持つ)というヘルパオブジェクトを保持する。

-(int)numberOfRowsInTableView:(NSTableView *)aTableView

dataSourseは表示を行なう行数を返す

-(id)tableView:(NSTableView *)aTableView
 objectValueForTableColumn:(NSTableColumn *)aTableColumn
 row:(int)rowIndex;

dataSourseは、カラムaTableColumn中の行rowIndex中に表示するオブジェクトを返す(Get)。

  • (void)tableView:(NSTableView *)aTableView
     setObjectValue:(id)onObject
     forTableColumn:(NSTableColumn *)aTableColumn
     row:(int)rowIndex;

dataSourseが、ユーザが入力したonObjectをカラムaTableColumn中の行rowIndexのデータとして受け取る(セット)。

通常のブログラマはテーブルビューに対して「3行目の第5カラムで7を表示せよ」といった制御を行なおうとするが、これは間違い。
テーブルビューは、3行目の第5カラムを表示する準備を整えた後、dataSourseに対して表示するオブジェクトを照会する。

では、情報が更新されたことは、どのようにしてテーブルビューに伝えるか。
→テーブルビューに対してreloadDataを送信する。

[tableView reload];

キーバリューコーディング

変数名のナマエによって、該当変数を読み書きするメソッド。
valueForKeyで取得、takeValueでセット。

-(id)valueForKey:(NSString *)attrName
-(void)takeValue:(id)newValue forKey:(NSString *)attrName

キーバリューコーディングがない場合は、

if([identifier isEqual:@"foo"]){
 return [employee foo];
}
if([identifier isEqual:@"bar"]){
 return [employee foo];
}
//以下続く…

となる(それぞれについて1つ1つ書くことになる)。

デリゲート

Cocoaフレームワーク中の多くのオブジェクトがdelegateアウトレットを持っている。
テーブルビューでは、選択を変更するための許可をそのdelegateにデリゲート(委譲)することができる(行のインデックスが偶数である場合のみ許可を与える、等)。

NSTableViewからdelegateへ委譲されるメッセージの一覧

-tableView:shouldSelectRow:
-tableView:didClickTableColumn:
-tableView:didDragTableColumn:
-tableView:mouseDownInHeaderOfTableColumn:
-tableView:shouldEditTableColumn:row:
-tableView:shouldSelectTableColumn:
-tableView:willDsplayCell:forTableColumn:row:
-tableViewColumnDidMove:
-tableViewColumnDidResize:
-tableViewSelectionDidChange:
-tableViewSelectionIsChanging:

NSCorder

NSCorder(コーダー)とは、バイトストリームを抽象化したもの。
コーダーを用いることによって、そこにデータを書き込んだり、そこからデータを読み出したりできる。

あるクラスにNSCodingを実装する場合、具体的には以下のメソッドを実装する。

-(id)initWithCoder:(NSCoder *)coder

コーダーから読み込む。

-(void)encodeWithCoder:(NSCoder *)coder

コーダーに書き込む。

保存(エンコード)

ファイルの書き出しに必要なメソッド

-(NSData *)dataRepresentationOfType:(NSString *)aType

読み込み(デコード)

ロードに必要なメソッド

-(BOOL)loadDataRepresentation:(NSData *)docData ofType:(NSString *)docType

まずデータがロードされ、それからnibファイルが読み込まれる。
そのため、nibファイルの読み込みが終わると、ドキュメントオブジェクトに対して、以下のメッセージが送信される。

-(void)windowcontrollerDidLoadNib:(NSWindowController *)aController

環境設定パネル

AppControllerという名前の新しいObjective-Cクラス(ファイル)を追加。
.hの冒頭を、

#import <Cocoa/Cocoa.h>
@class PreferenceController;

とし、PreferenceControllerというクラスが存在することをコンパイラに伝える。
これは、

#import <Cocoa/Cocoa.h>
#import "PreferenceController.h"

と同じだが、これだとコンパイラは多くのファイルを解析しなければならない。
@classを使用した方がビルドが高速。

NSDictionary/NSMutableDictionary

NSDictionaryは変更不可(照会のみ)。
NSMutableDictionaryはキーと値の追加や削除が可能。

キーはユニーク。

キーから値を取得するには、

anObject = [myDictionary objectForKey:@"foo"];

とする(該当キーが存在しない場合はnilが返る)。

-(NSArray *)allKeys

ディクショナリ中のキー全てを保持した新たな配列を返す。

-(unsigned)count

キー/値ペアの総数。

-(id)objectForKey:(NSString *)aKey

aKeyに対応付けられた値を返す。

ディクショナリ中の全てのキーをログ出力するには、

NSString *s;
NSEnumerator *e = [myDict keyEnumerator];
while(s = [e nextObject]){
 NSLog(@"Key is %@", s);
}

NSArrayクラスも列挙子を生成可能。

-(NSEnumerator *)objectEnumrator
+(id)dictionary

空のディクショナリを生成。

  • (void)removeObjectForKey:(NSString *)aKey

aKeyとその値のオブジェクトを削除。

  • (void)setObject:(id)onObject forKey:(NSString *)aKey

追加。

NSUserDefaults

出荷時のデフォルト設定と異なっているもののみ、ユーザデフォルトデータベースに格納される。

+(NSUserDefaults *)standardUserDefaults

アプリケーション内で共有されるデフォルトオブジェクトを返す。

-(void)registerDefaults:(NSDictionary *)dictionary

アプリケーションにおける出荷時のデフォルトを登録。

-(void)setBool:(BOOL)value forKey:(NSString *)defaultName
-(void)setFloat:(float)value forKey:(NSString *)defaultName
-(void)setInteger:(int)value forKey:(NSString *)defaultName
-(void)setObject:(id)value forKey:(NSString *)defaultName

セット。

-(BOOL)boolForKey:(NSString *)defaultName
-(float)floatForKey:(NSString *)defaultName
-(int)integerForKey:(NSString *)defaultName
-(id)objectForKey:(NSString *)defaultName

ゲット。

  • (void)removeObjectForKey:(NSString *)defaultName

削除。

通知

実行中のアプリケーションには、NSNotificationCenterのインスタンスが存在している。

NSNotificationCenter

+(NSNotificationCenter *)defaultCenter

NSNotificationCenterオブジェクトを返す。

-(void)addObserver:(id)onObserver
 selector:(SEL)aSelector
 name:(NSString *)notificationName
 object:(id)anObject

anObjectというオブジェクトを伴ったnotificationNameという名前の通知を受け取るanObserverを登録。
anObserverには、aSelectorというメッセージが送信される。
※notificationNameがnilの場合、適合する全てのオブザーバに対して通知が送信される。
※anObjectがnilの場合、名前がnotificationNameである全てのオブザーバに対して通知が送信される。

-(void)postNotification:(NSNotification *)notification

NSNotificationCenterオブジェクトに通知をポスト。

-(void)postNotificationName:(NSString *)aName
 object:(id)anObject

通知を生成し、ポスト。

-(void)removeObserver:(id)observer

オブザーバ一覧からobserverを削除。

アラートボックス

モーダルとなる。

int choice = NSRunAlertPanel(@"タイトル", @"文章", @"YES", @"NO");

NSView

ウィンドウはNSViewのサブクラスではない。

-(NSView *)superview
-(NSArray *)subviews
-(NSWindow *)window

NSResponder(マウス編)

イベントを取り扱うメソッド(マウスイベント/キーボードイベント…)が宣言されている。
NSViewを継承。

-(void)mouseDown:(NSEvent *)theEvent
-(void)rightMouseDown:(NSEvent *)theEvent
-(void)otherMouseDown:(NSEvent *)theEvent
-(void)mouseUp:(NSEvent *)theEvent
-(void)rightMouseUp:(NSEvent *)theEvent
-(void)otherMouseUp:(NSEvent *)theEvent
-(void)mouseMoved:(NSEvent *)theEvent
-(void)mouseDragged:(NSEvent *)theEvent
-(void)scrollWheel:(NSEvent *)theEvent
-(void)rightMouseDragged:(NSEvent *)theEvent
-(void)otherMouseDragged:(NSEvent *)theEvent
-(void)mouseEntered:(NSEvent *)theEvent
-(void)mouseExited:(NSEvent *)theEvent

NSEvent

イベントの情報がすべて保持されている。

-(NSPoint)locationInWindow

マウスイベントが発生した位置。

-(unsigned int)modifierFlags

修飾キーの判別。
例:

-(void)mouseDown:(NSEvent *)e
{
 unsigned int flags;
 flags = [e modifierFlags];
 if(flags & NSControlKeyMask){
  //コントロールキーが押されている
 }
 if(flags & NSShiftKeyMask){
  //シフトキーが押されている
 }
}

修飾キー:

NSAlphaShiftKeyMask
NSShiftKeyMask
NSControlKeyMask
NSAlternateKeyMask
NSCommandKeyMask
NSNumericPadKeyMask
NSHelpKeyMask
NSFunctionKeyMask
-(NSTimeInterval)timestamp

マシンがブートされてからイベントが発生した時までの時間(秒)を返すメソッド。
NSTimeIntervalはdouble型。

-(NSWindow *)window

イベントの発生したウィンドウ。

-(int)clickCount

シングルクリック、ダブルクリック…。

-(float)pressure

タブレット等の時の圧力(値は0〜1)。

-(float)deltaX
-(float)deltaY
-(float)deltaZ

スクロールホイールの変化量。

座標系

ビューbの座標系上にあるNSPoint pを、ビューaの座標系上にあるqに変換するには、

NSPoint q = [a convertPoint:p fromView:b];

となる(bがnilの場合は、ウィンドウの座標系に変換される)。

オートスクロール

ユーザがドラッグを行なった際、スクロールビューに対してautoscroll:というメッセージを送信する。

NSResponder(キー編)

NSResponderから継承されるメソッド。

-(BOOL)acceptsFristResponder

キーボードイベントを取り扱う場合はYESを返すようにこのメソッドをオーバーライドする。

-(BOOL)resignFiestResponder

レシーバがファーストレスポンダの地位を放棄するかどうかを照会するメソッド。

-(BOOL)becomeFirstResponder

レシーバがNSWindowのファーストレスポンダになろうとしていることを連絡するメソッド。

-(void)keyDown:(NSEvent *)theEvent

ユーザがキーを押したことをレシーバに知らせるメソッド。

-(void)keyUp:(NSEvent *)theEvent

ユーザがキーを離したことをレシーバに知らせるメソッド。

-(void)flagsChanged:(NSEvent *)theEvent

ユーザが修飾キーを押したり離したりしたことをレシーバに知らせるメソッド。

NSEvent(キーボード編)

  • (NSString *)characters
  • (BOOL)isARepeat
  • (unsigned short)keyCode
  • (unsigned int)modifierFlags

NSFont

+(NSFont *)fontWithName:(NSString *)fontName size:(float)fontSize

フォントオブジェクトを返す。
フォントサイズに0.0を指定すると、そのユーザのデフォルトサイズを返す。

+(NSFont *)userFixedPitchFontOfSize:(float)fontSize
+(NSFont *)userFontOfSize:(float)fontSize;
+(NSFont *)messageFontOfSize:(float)fontSize;
+(NSFont *)toolTipsFontOfSize:(float)fontSize;
+(NSFont *)titleBarFontOfSize:(float)fontSize;

それぞれのデフォルトフォント。
フォントサイズに0.0を指定すると、そのユーザのデフォルトサイズを返す。

-(float)descender

最長のディセンダ(文字のベースラインから下に出る部分)における最下部のy座標を返す。

-(float)ascender

 最上部のy座標。

 ***文字列の描画

 NSStirngとNSAttributedStringには、ビュー上に描画を行なうためのメソッドがある。

  -(void)drawAtPoint:(NSPoint)aPoint

 aPointは文字列の左下。

  -(void)drawInRect:(NSRect)aRect

aRectより文字列画の方が大きくなった場合は、クリップされる。
-(NSSize)size

描画した時のサイズを返す。

NSStringの場合は、適用する属性ディクショナリを指定する必要がある。

-(void)drawAtPoint:(NSPoint)aPoint
 withAttributes:(NSDictionary *)attribs
-(void)drawInRect:(NSRect)aRect
 withAttributes:(NSDictionary *)attribs
-(NSSize)sizeWithAttributes:(NSDictionary *)attribs

PDF

以下のメソッドがNSViewに用意されている。

-(NSData *)dataWithPDFInsideRect:(NSRect)aRect

このメソッドは、データオブジェクトを生成し、その後drawRectを呼び出し、その描画コマンドがデータオブジェクトに書き込まれる。
これをファイルに保存すればPDFになる。

コピー&ペースト

/System/Library/CoreServices/pbs(ペーストボードサーバ)が実行されている。
NSPasteboardクラスで読み書きする。
同じデータを複数の形式でコピー可能(読み込み側のアプリが適切な形式を選択して利用する)。

NSPasteboard

+(NSPasteboard *)generalPasteboard

汎用のNSPasteboardを返す。

+(NSPasteboard *)pasteboardWithName:(NSString *)name

nameで指定されたペーストボードを返す。
nameに指定できる標準ペーストボードのグローバル変数は以下。

NSGeneralPboard
NSFontPboard
NSRulerPboard
NSFindPboard
NSDragPboard
-(int)declareTypes:(NSArrary *)types owner:(id)theOwner

theOwnerが書き出すデータの型を宣言。
標準型は以下。

NSColorPboardType
NSFileContentsPboardType
NSFilenamesPboardType
NSFontPboardType
NSPDFPboardType
NSPostScriptPICTPboardType
NSRulerPboardType
NSRTFPboardType
NSRTFDPboardType
NSStringPboardType
NSTabularTextPboardType
NSTIFFPboardType
NSURLPboardType

ペーストボードにデータを書き出すには、

-(BOOL)setData:(NSData *)aData forType:(NSString *)dataType
-(BOOL)setString:(NSString *)aString forType:(NSString *)dataType

ペーストボードから読み込み可能なデータ型の配列を取得するには、

-(NSArray *)types

読み込み可能なtypesの中から最初に発見した型を返すには、

-(NSString *)availableTypeFromArray:(NSArray *)types

ペーストボードからのデータの読み込み。

-(NSData *)dataForType:(NSString *)dataType
-(NSString *)stringForType:(NSString *)dataType

ドラッグ&ドロップ

ドラッグ&ドロップは派手なコピー&ペースト。
ドラッグが開始されると、ドラッグ用ペーストボードにコピーされ、ドロップされるとドラッグ用ペーストボードから読み取られる。

アプリ間のドラッグ&ドロップの場合、「何もしない」「データのコピー」「リンクの生成」の2種類がある。

NSDragOperationNone
NSDragOperationCopy
NSDragOperationLink

ビューにドラッグ&ドロップを実装する場合、「ビューをドラッグ元にする」「ビューをドラッグ先にする」の2つの処理が必要。

ドラッグ元にするには、ビューにdraggingSourceOperationMaskForLocal:を実装。

ドラッグ先になる(ドラッグを受け入れる)には、まず受け入れ宣言。

-(void)registerForDraggedTypes:(NSArray *)pboardTypes

NSViewに用意されている上記をinitWithFrame:から呼び出す。

さらに6つ(!)のメソッドを実装する必要あり。

draggingEnterd:(入った)
draggingUpdated:(入ってる)(オプション)
dragginExited:(通り過ぎた)
prepareForDragOperation:(ドロップされた)
performDragOperation:(ドロップされる準備ができた)
concludeDragOperation:(ドロップされた)

シート

NSApplicationにシートを表示するためのメソッドがある。

-(void)beginSheet:(NSWindow *)sheet
 modalForWindow:(NSWiindow *)docWindow
 modalDelegate:(id)modalDelegate
 didEndSelector:(SEL)didEndSelector
 contextInfo:(void *)contextInfo;

シートを閉じるには、

-(void)endSheet:(NSWindow *)sheet returnCode:(iint)returnCode;

didEndSelectorで起動されるメソッドは、以下になっている必要がある(メソッド名や引数名はなんでも良い)。

-(void)rex:(NSWindow *)sheet
 fido:(int)returnCode
 rover:(void *)contextInfo

↑ページトップへ

  • Objective-C入門
    http://wisdom.sakura.ne.jp/programming/objc/
  • Cocoa Club(Cocoaのメモリ管理1〜4)
    http://wwwa.dcns.ne.jp/〜nito/CocoaClub/
  • Mac OS X Dev-jp ML
    http://www.tech-arts.co.jp/macosx/macosx-dev-jp/log.html