Objective-Cの特徴は、C言語を母体としたオブジェクト指向言語であるということであるが、重要な点が2つある。
基本的にはC言語と同じ言語だが、当然クラスが存在するため、その点は非常にコーディング時に注意が必要である。C言語と同じフォーマットな為、関数を利用するのに、.h ファイルに関数の定義文を記述するように、クラスにも定義文が存在する。
@interface Class名 : 継承Class名 { int xx; int yy; } - (void) set:(int)x y:(int)y; - (int) get_xx; - (int) get_yy; @end
このように @interface の後ろにクラス名、「:」の後ろに継承クラスを指定する。通常は Object クラスを継承クラスとし、OSXなどの場合には NSObject を利用する。{}の中がクラスで利用する変数であり、{}の後ろの - (マイナス)で始まる行が、クラスのmethod定義である。(+(プラス)で始まる行も存在するが、他の文献参照の事)
ここで非常に分かりづらい表記が存在する。たとえばこの、set メソッドを呼び出すときは以下のような表記になる。
id objectInstance = [objectInstance new]; (newはallocとinitの両方を実行する) [objectInstance set:100 y:200];
これは、1行目で objectInstance というid型(オブジェクト型)の変数を定義し、インスタンスを作成したあと、2行目で set メソッドを呼び出しているが、おもしろいのがメソッド名と引数が同じレベルで定義されるということである。
他の言語では method( 引数1, 引数2 )のような、メソッド名は特別な位置に存在するが、Objective-Cではメソッドと引数という概念ではなく、メッセージ名という感覚で捉えていた方が良く理解できる。
要するにObjective-Cはクラス内変数に直接アクセス出来ず、メッセージパッシング型呼び出しを利用している関係上、オブジェクトに対するアクションはすべてメッセージとして処理され、他の言語でいうメソッド名や引数名などもすべてメッセージとして処理されている、よって上記の[objectInstance set:100 y:200]は以下のように読み取れる。
[メッセージを送るオブジェクト メッセージ1:対応する値 メッセージ2:対応する値]
要は、メソッド名という認識というより、メッセージなのである。
たとえば、
- (void) set:(int)x y:(int)y; - (void) set:(int)x yy:(int)y;
の2つのメソッド定義は、他の言語の感覚ではsetメソッドに引数としてyを渡すメソッドとして認識できるが、これはObjective-Cでは明確に2つのメソッドとして処理される。
[object set:100 y:200]; [object set:100 yy:200];
この2つのメソッド呼び出しは別のメソッドとして定義されているため、下記で説明する@imprementation 行に別々にメソッドの定義が無いと、エラーとなる。
objective-Cでは、型のチェックを行う上で、他のファイルからのclassを参照するときに、#import 文を利用してそのクラス定義を読み込む機能があるが、ファイルを読み込むほど必要がないような場合、@classを利用することで、最低限のclass定義をコンパイラに伝えることができる。アップルのサイトによれば、
という使い方ができるとの事である。ちなみに、同じプロジェクトファイル内にあれば、どのフォルダにclass定義ファイルがあっても、自動的に見つけてくれるようだ。
Objective-C言語におけるクラスの実装文は .m の拡張子を持つファイルに保存する。
@implementation Class名 : 継承Class名 - (void) set:(int)x y:(int)y { xx = x; yy = y; } - (int) get_xx { return xx; } - (int) get_yy { return yy; } @end
実装文では、実際のメソッドとしてクラスの振る舞いの定義を行う。先ほどの説明の通りにメソッド名という概念よりも、メッセージ名の組み合わせという別にメソッドを記述という認識のほうがより近い。例えばここで記述している set メソッド(そもそもこの言い方が変であるが)は、「set メッセージと y メッセージを組み合わせたら、この振る舞いを行う」というように解釈する方がより適切であろう。(※ちなみに引数の型が違っても、別の定義として扱われるので注意が必要)
Objective-C言語ではクラスの変数に直接外部からアクセスできないので、変数値を取得するための簡単なメソッドが書かれる場合があり、このようなメソッドを特にアクセッサメソッドと呼ばれる。(上記の例では get_xx などのメソッドを指す)このアクセッサメソッドの場合(クラス変数の型を戻り値に持っているようなもの)では、より簡単な記述方法として、以下のようなものがある。
通常の指定
cnt = [object get_xx];
簡単な記述方法
cnt = object.get_xx;
※この指定方式でも、実際には定義済みのアクセサメソッドがメッセージパッシングされ、実行されている。
またこのアクセッサメソッドをいちいち、クラス変数別に記述するのが面倒な場合には、@property と @synthesize ディレクティブを利用すれば、かなりの記述を省略できる。詳細は他の文献を参照のこと。
Objective-cによる定数の定義には以下のような代表的な定義がある。
この定義方法は、プロプロセッサを利用した方法で、言語としての厳密な意味での定数の定義ではないが、非常によく使われる方法である。
#define TEISUU 100
これは、TEISUU という名前を、100という文字に置き換える定義である。また当然のことながらプリプロセッサ構文なので、最後に「;」が付かない
主に、浮動小数点値の定数を作成するにはconst定義を使用する。また、他の定数に関連のない、整数の定数を作成するためにもconstを使用することができる。
enumは、連番をつける場合などに利用される。
typedef enum _InputMode { InputModeNone = 0, InputModeAdd = 1, InputModeDelete = 2, InputModeUpdate = 3 } InputMode;
上記のように、特に定数群をグループとして管理する場合などに利用する。
特徴でも述べたように、Objective-C言語では直接クラス変数にアクセスすることは、推奨されていないが、スコープ定義である@public、@private、@protected をクラス変数に定義することでアクセスすることは可能である。その際には -> 演算子を使って以下のようにアクセスされる。
obj->xx
各スコープ定義の可視範囲の定義においては、他の文献を参考にしてほしい。
Objective-Cでは、@property ディレクティブと @synthesizeディレクティブを利用することで、プロパティへの参照アクセッサメソッドを自動的に作成することができる。ようするに
@interface testCls : NSObject { int cnt; NSString *name; } @property (assign) int cnt; @property (retain) NSString *name; @end
このようにクラス定義の@interfaceの中に定義されたクラス変数を@property定義することで、アクセッサメソッドの記述を省略できる。またその際の実装(@implementation)では以下のように@synthesizeでその存在を明記する。
@implementation testCls : NSObject @synthesize cnt; @synthesize name; @end
このように実装部分を記述しなくても指定したクラス変数にアクセッサメソッドが定義されるので以下のように利用できる。
testCls tst = [testCls new]; [tst setCnt:200]; [tst setName:@"Test Message];
ここでは、setter(値を設定するメソッド)として、setCnt とsetName というメソッドが追加されていることが分かる。この@propertyと@synthesizeの機能により追加されるsetterメソッドは、プロパティ名の頭にsetが必ず付き、プロパティ名の頭の文字が大文字になるルールとなっている。
逆にgetterといわれる、値を取得するアクセッサメソッドは、上記の例でいえば、tst.cnt という名前でcnt変数を取得できる。ようするにgetterのアクセッサメソッドは名前がそのままとして定義されるということである。
@propertyの後ろに付いている(assign)や(retain)などの属性は、以下のような種類が存在する。
assign | 基本的な属性タイプ、このキーワードを使う場合、アプリケーションはガーベジコレクション(GC)を使っていないと、単純代入は適切な振る舞いではなくなるためコンパイラ警告が発生する、よって格納方法の属性の1つを明示的に指定する必要がある。また変数がNSCopyingプロトコルを採用していると、assignの使用は適切でないためWarningとなる。 |
retain | オブジェクトの型の場合は主にこれを利用する、以前の内容を一度、release(メモリ解放)してから引数のオブジェクトを代入参照する。ガーベージコレクション利用時は利用しない指定方法(メモリ解放を自動で行う為) |
copy | 参照ではなく複製で作成され代入される。基本的にNSStringなどはこれを利用する |
readwrite | 読み書き型なので、setter,getterの両方のメソッドが作成される(初期値) |
readonly | getterだけ作成される |
write | setterだけ作成される |
getter | 指定した名前でgetterを作成 |
setter | 指定した名前でsetterを作成 |
nonatomic | 合成されるアクセサを非アトミックとする。初期値は合成されるアクセサはすべてアトミックであり、これはマルチスレッド環境でプロパティへの堅牢なアクセスを可能にすることを意図している。(スレッドが同時に実行しているかどうかに関係なく必ず完全に取得または設定される事。詳細はここ) |
この属性については下記のように複数の指定が可能。
@property (assign, readwrite) int cnt;
またsetterやgetterの名前をここで指定することも可能。
@property (getter=getCounter, setter=setCounter:) int cnt;
Objective-C言語には共通クラス変数は存在しないため、そのクラスのすべてのインスタンスが共有する変数というものがほしい場合には、クラスを宣言するヘッダファイル内にファイルスコープのグローバル変数を用意し、このグローバル変数をインスタンスメソッドなどからアクセスするという形でクラス変数を実現する。
Objective-Cでは、メモリ管理にソードには存在しないが所有者という概念がある。
NSString * name = [[NSString alloc] initWithString:@"MaName"];
この行により、nameオブジェクトはself(このコードが実行されているクラスインスタンス)によって確保されているので、このnameの所有者はselfとなる。よってselfでは自分でこのnameのメモリ解放を行う必要がある。
-(void) dealloc { [name release]; // 通常はこのようにdealloc呼び出しで処理する [super dealloc]; }
このように所有者がallocやnewによって明示的にされている場合は分かりやすいが、次のような場合は、所有者がいないということになる。
NSString * name = [NSString stringWithString:@"MaName"];
この場合、このコード
[Cmd]+[/] | 選択行のコメントアウト |
[Cmd]+[l] | 選択行のインデント |
[Cmd]+[S] | 保存 |
[Opt]+[Cmd]+[↑] | ヘッダファイル(*.h)と実装ファイル(*.m)の切り替え |
[Cmd]+任意のシンボルをダブルクリック | 対象シンボルへジャンプ |
[Opt]+任意のシンボルをダブルクリック | 対象シンボルのAPIリファレンスを表示 |
任意の文字列を選択+[Cmd]+[E] | 検索対象をセット |
[Cmd]+[G] | 次を検索 |
[Cmd]+[F] | ファイル内から検索 |
[Cmd]+[Shift]+[F] | プロジェクト全体から検索 |
[Cmd]+[Shift]+[O] | ステップオーバー |
[Cmd]+[Shift]+[I] | ステップイン |
[Cmd]+[Shift]+[T] | ステップアウト |
[Opt]+[Cmd]+[P] | 次のブレークポイントまで流す |