最終更新:2014-03-26 (水) 01:00:27 (3824d)
Objective-C
Top / Objective-C
マジ変態。
概要
- Objective-Cは、Smalltalkを基礎とした非常に動的な言語で、コンパイラはオブジェクト自体について多くの情報を実行時まで決定しない (動的バインディング)
- Objective-CはC言語を拡張したものなので、CocoaのC言語の関数とかも混ぜて使う。
- オブジェクト指向プログラミングをサポートするために、(Smalltalkから派生した)構文とセマンティクスにかかわるいくつかの機能が拡張されている
Cocoa
- OPENSTEPを基に開発されているMac OS X向けのフレームワーク(API)
- Objective-Cはコンパイル時ではなく実行時に決定できる事項が多い。これこそが、同じく動的な特性をフルに活用している、Cocoaフレームワークとベストマッチとなる
開発ツール
- Xcode - 統合開発環境。 Mac App Storeからダウンロードできる。
- Interface Builder - GUIの設計をするためのツール。
Appleのドキュメント (ADC)
- PDFも用意されている。
日本語 英語 Objective-Cによるプログラミング (132ページ) 一番とっつきやすいかも。 Programming with Objective-C? Objective-Cプログラミングの概念 (89ページ) Concepts in Objective-C Programming Objective-C プログラミング言語 (113ページ) The Objective-C Programming Language
特徴とか
NSObject
オブジェクト識別子 id
- (Objective-C ではオブジェクトはすべてポインタとして扱う。従ってオブジェクトの変数の宣言や引数の型としてオブジェクトを宣言するときには常に * が付くが、オブジェクトなら種類を問わず何でも格納できる id という型があり、この型には * は付かない。)
- Objective-Cは原理的に、id型のオブジェクトを実行時に判定する、動的型付けの言語である。
オブジェクトメッセージング
- 何か処理を呼ぶ時は関数呼び出しという形ではなく、メッセージという形でオブジェクト同士のやり取りを行う。
[receiver message]
- でターゲットにメッセージを送る。
- ターゲットにmessageメソッドが実装されていればそのメソッドが呼び出される。
NSString* string; // NSStringのインスタンスを確保して代入 string = [[NSString alloc] init];
- alloc や init というのがメッセージ。関数呼び出しとはちょっと違う
- CocoaのCの関数は普通にNSMakeRange(2,3);のように呼び出す。
nil
- キーワードnilは、NULLオブジェクト、すなわち値が0のid
- Objective-Cでは、nilへのメッセージ送信が可能(何もおこらない)
- メソッドがオブジェクトを返す場合は、nilに送信されたメッセージは0(nil)を返します
- オブジェクトを指すポインタの初期値を指定しなければ、コンパイラが自動的に、nilに初期化
メモリ管理
- メモリ管理のための3つの仕組みが提供されている
ARC (Automatic Reference Counting) - 自動参照カウント?
- iOS 5で導入。
- LLVMコンパイラがコンパイル時にretain, release, autoreleaseを挿入してくれる
MRC (Manual Reference Counting?) - 非自動参照カウント?
GC
- 自動的にメモリ解放とか。
- Objective-C 2.0から搭載。NSGarbageCollector?
- Mac OS Xのみ。
動的バインディング
- 呼び出すメソッドを実行時に決定する事
関数呼び出しとメッセージの大きな違いは、関数とその引数がコンパイル時にコードの中で結合されるのに対して、メッセージと受信側のオブジェクトはプログラムを実行してメッセージが送信されるまで結合されないこと。したがって、メッセージに応答するために呼び出される実際のメソッドは、コードのコンパイル時ではなく、実行時にのみ知ることができる。
Cocoaバインディング
基本的な文法
C言語と共通の部分
コメント
// 一行コメント /* 複数行コメント */
データ型
int n; unsigned int n; char n; unsigned char n; long n; float n; double n;
変数
int i;//変数宣言 int i = 0;//宣言&代入
四則演算とか
i = 1 + 1; i = 1 - 1; i = 1 * 2; i = 1 / 2; mod = 4 % 2; i++; ++i; i--; --i;
if文
if ( 条件 ) { } else if ( 条件 ) { }
- オブジェクトの中身を比較する場合はisEqual?を使う。
while文
int i = 0; while (i < 5) { // なんか処理 i++; }
for文
for (int i = 0; i < 5; i++) { //処理 }
Objective-C/ディレクティブ
- Objective-Cで追加されている予約語は@から始まる。(C言語を拡張してあるため)
- @interface
- @implementation
- @class?
- @selector
- @protocol
- @property
- @try?~@catch?~@end?
- @"文字列"
- とか
関数の定義
- Cで書くと
void setWidthAndHeight(int width,int height);
- なのが
-(void) setWidth:(int)width andHeight:(int)height;
- となる。
- setWidth:がメソッド名で、andHeight:の部分はラベルという扱い。
//objectのsetWidth:andHeight:を呼び出す [object setWidth:width andHeight:height];
- このへんの命名規則がどうも慣れない。
変数の宣言
//型 変数名 id varName; NSString* str;
真偽値
- BOOL - YESかNO
typedef signed char BOOL; #define YES (BOOL)1 #define NO (BOOL)0
文字列
- 文字列はNSStringで扱う。
- @を使うとリテラルがNSStringになる(UTF-8)
NSString* str = @"Hello, Hoge";
- 文字列はstringWithString?とかで初期化。
NSString* str; str = @"hoge";//リテラル str = [NSString stringWithString:@"日本語"];//NSStringで初期化 str = [NSString stringWithCString:"hoge"];//C文字列で初期化
- 格納する内容によって、さまざまなNSStringのサブクラスが用意してあり、そのサブクラスは自動的に選択される。(このようなクラスはクラスクラスタと呼ばれる)
- 普通はNSCFString?とかになる(CFはCore Foundationの略)が、NSStringに対してパスの文字列操作([NSString stringByAppendingPathComponent?:]とか)を行うと、内部的にはNSPathStore2?になったりする。
NSString* str; str = [NSString alloc];//allocされた時点では未決定なのでNSPlaceholderString ・・・ //どのNSStringのサブクラスが選択されているか出力 NSLog(@"str1 is %@", NSStringFromClass([str1 class]));
リテラル
- @"文字列"はNSString
- Xcode 4.4からNSNumber, NSArray, NSDictionaryも@リテラルで書ける
コレクション
- Mutable~と付いて無いやつは内容の変更が不可。
配列
辞書
- NSDictionary、NSMutableDictionary?を使う
集合
例外処理
- ターゲットの設定で、「Objective-C例外処理を有効にする」をオンにしないと警告が出る
@try{ //ここでなんらかエラーを発生させる エラーの起きるような処理 NSLog(@"hoge");//ここは実行されない } @catch(NSException* ex){ NSLog(@"name %@,reason %@",[ex name],[ex reason]); }
クラスの書き方
- ヘッダ(.h)に@interfaceで宣言をして実装は@implementationで.mファイルに行う
クラスの宣言(hoge.h)
- @interfaceでクラスを定義する
- -がインスタンスメソッド
- +がクラスメソッド
@interface HogeObject:NSObject // @interface クラス名:親クラス名 { //独自のインスタンス変数を宣言 id _hogeValue; //インスタンス変数は _から始めると区別しやすい(推奨) NSString *huga; } //プロパティの宣言 //メソッドの宣言 //-がインスタンスメソッドで、 //+がクラスメソッド。 -(id) init;//イニシャライザ //アクセッサ -(void) setHoge:(id)hoge; -(id) hoge; @end
- 公開しない関数はヘッダに書かなくても良い
- Objective-C にはコンストラクタは存在しない。初期化はイニシャライザ(init)で行う
- 暗黙の変数
- self
- super
- オブジェクトを指すポインタの初期値を指定しなければ、コンパイラが自動的に、nilに初期化
クラスの実装(hoge.m)
- @implementation でクラスを実装する
#import "hoge.h" @implementation HogeObject //イニシャライザ - (id)init { [super init]; //ここで初期化 return self; } //アクセッサ -(void) setHoge:(id)hoge { _hogeValue = hoge; } -(id) hoge{ return _hogeValue; } @end
アクセッサ
- Objective-Cでは、すべてのメンバ変数はprivate(参照できるけど・・)。メンバ変数にアクセスする場合はアクセッサ(getter、setter)を使う。
- setterはsetXXX
- getterはXXX(get~にしない)
- とする。
- 下記のプロパティを使って合成(自動実装)することがほとんど。
プロパティ
- 変数ごとにいちいちアクセッサかいてたら面倒なので、@propertyという仕組みが用意されている。
- Objective-C 2.0から@propertyや@synthesizeが導入された。
- プロパティを宣言すれば、アクセサメソッドとインスタンス変数が自動的に生成される
- 特に指定しなければ、合成されるインスタンス変数の名前は、プロパティ名の先頭にアンダースコアを置いたものになる
- @interface
- @propertyでプロパティを宣言
- @implementation
- アクセッサをどうするか@synthesizeか@dynamicで指定
- @synthesize指示子にプロパティ名を指定すると、アクセッサメソッドを自動的に「合成」してくれる
- プロパティに対応するgetterメソッドとsetterメソッドのデフォルトの名前は、XXX,setXXX
- 手動で実装するときは自分でXXX,setXXXを実装する
- コンパイル時にはアクセッサメソッドが存在しないが、実行時に動的にそれらが追加される、というケースに対応するために、@dynamicという指示子も用意されている。
- Xcode 4.4〜
- @synthesizeを省略可能
- @synthesizeも@dynamicも書かなかった場合には@synthesizeを付けたのと同じ扱いとなる
// ViewController.h @property (strong) NSString* testString; // ViewController.m @synthesize testString;//@synthesize testString = _testString;
- ↓
// ViewController.h @property NSString* testString;
- デフォルトがstrongなので書かなくてよくなった
- @synthesizeされる相手のインスタンス変数は、同名の変数ではなくてアンダーバーを最初に付けた物がデフォルト
- ブール型(値がYESまたはNO)のプロパティには、ゲッタメソッドを「is」で始まる名前にする慣習がある
@property (getter=isFinished) BOOL finished;
ドット構文?
- アクセサメソッドの呼び出しを明示的に記述する代わりに、ドット構文を使ってもオブジェクトのプロパティにアクセスできる。
- オブジェクトに関して使用される場合、ドット構文は「構文上の便宜」であり、コンパイラによってアクセサメソッドの呼び出しに変換されます
- 下記は同じ意味。
myInstance.value = 10; printf("myInstance value: %d", myInstance.value);
[myInstance setValue:10]; printf("myInstance value: %d", [myInstance value]);
- 自分のクラスで定義しているプロパティはselfキーワードを使って参照する必要がある。hoge ではインスタンス変数として扱われるが、self.hoge ではプロパティとしてアクセスされる。
キー・バリュー・コーディング(キー値コーディング)
- 変数名の文字列表記によって該当変数を読み書きするメソッド
//変数の値を取得 -(id)valueForKey:(NSString *)attrName //変数の値をセット -(void)takeValue:(id)newValue forKey:(NSString *) attrName
インスタンス化
HogeObject* objHoge; //id objHogeでも良い objHoge = [[HogeObject alloc] init]; //メモリを確保してイニシャライザで初期化
- C#で書くとこんな感じ?
HogeObject objHoge; objHoge = new HogeObject();//コンストラクタ
メソッドの呼び出し方
- setHoge,hogeというのはメッセージ。実装されていなければ何も起きない。
- allocやinitはNSObjectで実装されているから呼び出すこと!(Cocoaの前提)
//[オブジェクト名 メソッド名:引数1] HogeObject* objHoge; //インスタンス化して初期化(alloc と initはNSObjectで定義されている) objHoge = [[HogeObject alloc] init]; [objHoge setHoge: @"hoge"]; hoge = [objHoge hoge]; [objHoge setHoge: [objHoge hoge]];
- id は「任意のオブジェクトを参照するもの」として利用可能。ただし、コンパイル時にはクラスは確定していないので、クラスに存在するメソッドを利用しているかどうかのチェックは行われないことになる。
- (後述するプロトコルを使った場合、コンパイル時にメソッド利用の可否を確認できる。id<hogeProtocol> のように記述する)
- 生成と初期化は基本的に alloc と init を呼び出すことになるが、NSStringのstringWithFormat のようなクラスメソッドは生成と初期化を行う。このようなメソッドを「コンビニエンスコンストラクタ」と呼ぶ。(最近のドキュメントだとファクトリーメソッドと呼んでるっぽい)
- メソッド名は得られるデータの形式で始まる。(例 NSStringのstringWithFormat, NSImageのimageNamed?)
カテゴリ
- サブクラス化を行わずに既存のクラスの機能を拡張できる強力な機能
- クラス名の後に()を付けてカテゴリ名を指定する。
- メソッドをカテゴリ分けすることもできる
@interface クラス名 (カテゴリ名) //メソッドの宣言 @end @implementation クラス名 (カテゴリ名) //メソッドの実装 @end
- NSStringなど、既存のクラスにカテゴリを追加して拡張することも可能。→サブクラス化よりお手軽
- カテゴリ独自のインスタンス変数は宣言できない。
プロトコル
- オブジェクトの振る舞いを表すメソッドの集合を定義
- @protocolという指示子で宣言。
- クラスの宣言時にプロトコルを指定することにより、オブジェクトの振る舞いが分かりやすくなる
- プロトコルが指定してあるのにメソッドが足りないとコンパイル時にエラーが出る
- プロセス間通信(ドラッグ&ドロップとか)のときに便利
- conformsToProtocol?:で、あるオブジェクトがプロトコルに適合しているかどうかを実行中に調べることもできる。
//プロトコルの定義(どこかのファイルに書く) @protocol HogeProtocol - (void)encodeWithCoder:(NSCoder*)coder; - (id)initWithCoder:(NSCoder*)decoder; @end
HogeObject?はHogeProtocol?に準拠(hoge.hに書く)//HogeObjectクラスの宣言にHogeProtocolを追加 @interface HogeObject : NSObject <HogeProtocol> { ... } ... @end
カテゴリのことを非形式プロトコル、@protocolのことを形式プロトコルと呼ぶこともある
クラスクラスタ
- NSStringなどのクラスは、フレームワーク内ではさらにそのサブクラスが定義されていて実際にはそのクラスのインスタンスが作られる。こうした複数の隠れたクラスが生成されるようなクラスを「クラスクラスタ」と呼んでいる。
セレクタ
- メソッドを示す変数。メソッドを指定するために使う。
- オブジェクトはメッセージを受け取り、該当するセレクタが存在すればそのセレクタのメソッドを実行する。
- メソッドを宣言すると、同名のセレクタが有効になる。
- 型はSEL型。取得には@selector指示子を使う
//nameというメソッドを指定 SEL selector selector = @selector(name); // objectがメソッドnameを実装しているかどうかを確認 if ([object respondsToSelectlr:@selector(name)]) { ... }
メソッドの呼び出しは直接行ってもいいし、セレクタを指定して呼び出すこともできる。// メソッドを呼び出す [object performSelector:@selector(name)];
- IMP型(関数へのポインタ)を利用すると、セレクタを介さずに直接関数を呼び出すことができる。
設計
イベントの処理
イベントの処理方法にはたぶん3種類ある。
アウトレット(IBOutlet)とアクション(IBAction)で関連付け
- ボタン押したときとかの処理
- Interface BuilderでGUIをデザインして、Xcode/Assistant Editorを使ってコードと結びつける。
@interface{ IBOutlet NSTextField textField;//IBで接続したNSTextFieldのアウトレット } //ボタンを押したときのアクションにbuttonPushed:を追加し、このIBActionと接続する -(IBAction)buttonPushed:(id)sender;
アウトレット (IBOutlet)
- クラスからIB上のGUI部品を指定するときに使う。
IBOutlet NSTextfiled textField;
- みたいな感じで宣言。textFiledをウィンドウ上のどの部品と結び付けるかはIBで右ドラッグ。(Crtl押しながらドラッグ)
アクション (IBAction)
- アクション=イベントの通知
- GUIによって呼び出されるメソッドのこと。
-(IBAction)someAction:(id)sender;
- と言う感じで宣言。
デリゲートでイベントを委譲するクラスを指定
- Xcodeからクラスを作成しIBに追加、File's Ownerのdelegateをその追加したクラスに設定する
- File's Owner(デフォルトではNSApplication)で処理されなかったイベントの処理は上で設定したクラスに委譲される
//デリゲートメソッド // アプリケーションが終了する前のイベント - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender{ //なんか処理 } // 最後のウィンドウが閉じた時のイベント - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender{ //なんか処理 }
Cocoa/デリゲート
- ほとんどの表示アイテムはマウスとキーボードイベントに対応するデリゲートを持つ
- IBでコントロールのデリゲートを設定すると、発生したイベントに応じてデリゲートへの問い合わせが起きる
- デリゲートにはカテゴリ(非形式プロトコル)を使う
- デリゲートは一つのオブジェクトしか登録できない
ノーティフィケーション
- クラスをノーティフィケーションセンター(NSNotificationCenter)に登録するとイベントが通知される
- 例えばNSApplicationにはNSApplicationDidBecomeActiveNotification?とかがあるのでそれらを受け取るように設定できる
//セレクタで指定するメソッド -(void)someMethod:(NSNotification*)notification { NSLog(@"%@ is notified",[notification name]); } //起動時に通知の設定を行う -(void)awakeFromNib{ NSNotificationCenter* center; //センターの取得 center = [NSNotificationCenter defaultCenter]; //セレクタに通知名を設定 [center addObserver:self //通知先オブジェクト selector:@selector(someMethod:) //通知先メソッドのセレクタ name: TestNotification //通知の名前 object:nil] } //コードのどこかでTestNotificationというノーティフィケーションを通知 [[NotificationCenter defaultCenter]postNotificationName:@"TestNotification" object:self userInfo:nil];
XIB (旧称:NIB)ファイル
- Interface Builderでいじくるファイル。
- IBのインスタンスウィンドウに表示されるアイコン(クラス)は、アプリケーションが起動され"MainMenu?.nib"ファイルが読み込まれた時にインスタンスが生成される。
- 起動時にNSApplicationによってawakeFromNib関数が呼び出される。VBとかで言うところのForm_Load?
レスポンダ
- Cocoaのイベントを処理するオブジェクト
- レスポンダチェーン
- First Responder > ウィンドウ(NSWindow) > デリゲート > メインのウィンドウ > アプリケーションオブジェクト(NSApplication) の順で処理される
ダイアログの表示
- NSRunAlertPanelというCocoaの関数を使う
クラスオブジェクト
- すべてのオブジェクトは自分のクラスに関する情報をメタ情報として持っており、これをクラスオブジェクトという形で取得可能。
- 型は、id型か、クラスオブジェクトのための型Classを使う
Class cls; cls = [object class]; // インスタンスからクラスオブジェクトを取得 cls = [NSString class]; // クラスからクラスオブジェクトを取得 cls = NSClassFromString(@"anyClassName"); //クラス名からクラスオブジェクトを取得 // ランタイムAPIを使って、C文字列からClassを取得する cls = objc_getClass("anyClassName"); cls = objc_lookUpClass("anyClassName");
どのクラスに属するかの判定if ([object isMemberOfClass:[NSString class]]) { // NSStringクラスの場合 ... } if ([object isKindOfClass:[NSString class]]) { // NSStringクラスか、それを継承したクラスの場合 ... }
- Objective-Cのクラス(objc_class)は、結局のところC言語の構造体。
printf("class name %s\n", ((struct objc_class*)cls)->name);
みたいにして、クラスのフィールドにアクセス可能。
インスタンスの比較
- 同値:内容が一緒
- 同等:同じインスタンス
if(objA==objB){ //同じインスタンス(同等) } if([objA isEqual:objB]){ //内容が一緒(同値) }
ファイル入出力
Objective-C には
- テキストデータをテキストのまま保存し、テキストのまま読み込みをする
- テキストデータを plist という Cocoa で採用された新しい形式で保存し読み込みをする
- テキストデータおよびバイナリデータをバイアリデータとして保存し読み込みをする
という3つのファイル入出力形式が用意されている。
Objective-C 2.0
AppleはMac OS X 10.5においてObjective-C 2.0という名称で言語仕様の変更を行った。
- ガベージコレクションの導入
- プロパティの導入
- 高速列挙
- プロトコルの強化
- Class Extensions
- ランタイム構造の変更
クラスのposingは廃止された。その代わりにメソッドの交換(セレクタのマッピングを入れ替える)が公式に用意され、カテゴリと組み合わせることでほぼ同じ機能を実現できる。
新機能
Xcode 4.4
- @synthesizeを省略可能
- NSNumber, NSArray, NSDictionary も@リテラルで書ける
- privateメソッドの定義を省略可能
Xcode 4.2
Objective-C/ブロック構文
- Mac OS X 10.6から対応
戻り値の型 (^ブロック名)(引数);
ブロック名 = ^(引数){};
参考
- マイコミ 【コラム】 ダイナミックObjective-C - 文法を覚えるならここかなぁ
- Objective-C入門
- http://wisdom.sakura.ne.jp/programming/objc/index.html
- http://www.libjingu.jp/trans/clocFAQ-j.html
- http://d.hatena.ne.jp/fn7/20100203/1265207098
- http://www.oklab.org/program/objective-c_osx.html
Java使いのための「Objective-Cではこう書く」
参考
参考図書
- 参考図書/詳解Objective-C 2.0
- 参考図書/Mac OS X Cocoaプログラミング
- 参考図書/たのしいCocoaプログラミング?