このページは…
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…ユーザインターフェースオブジェクトによって起動されるメソッドのこと。
流れ
- 何かのサブクラス(Foo)を作る。
- Create Files For Fooで、Foo.hとFoo.mができる。
- 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
以下のメソッドが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