インスタンス変数を保有しているクラスの場合、その多くが初期値を必要とするでしょう。 初期化を忘れてしまった場合、適切に動作しない可能性があります。
一般的なオブジェクト指向型言語では、オブジェクトを安全に、確実に初期化するための方法としてコンストラクタを用意しています。 コンストラクタは、クラスのインスタンスが作成された直後に自動的に呼び出される特別なメソッドのことですが、Objective-C ではコンストラクタは存在しません。
その代わり、コンストラクタはルートクラスが仕様に基づいて提供する構造を採用しているため、ルートクラスの初期化用メソッドを明示的に呼び出さなければなりません。 Object ルートクラスや、Mac OS X の NSObject クラスでは、init という名前のメソッドがオブジェクトの初期化用メソッドとして定義されています。 この init メソッドのことをイニシャライザと呼びます。
- (id)init;
通常、オブジェクトをインスタンス化した場合は直後に init メッセージを送信しなければなりません。 この作業は自動的なものではなく、明示的にメッセージから呼び出さなければなりません。 一般的には次のように記述されています。
[[クラス名 alloc] init]
しかし、new メソッドを使えば上記の文を内部で実行してくれるため、イニシャライザが自動的に呼び出される仕組みを再現できます。 C++ 系の血筋のオブジェクト指向型言語の経験者であれば、こちらの方が馴染むかもしれません。
+ (id)new;
どちらを使うかは開発者の好みでしょう。 例え、イニシャライザが特に初期化するべき内容や処理コードがないクラスに対しても、将来の拡張などに対応するために統一した扱い方をするべきです。
Object ルートクラスを継承した新しいクラスを宣言した場合、init メソッドをオーバーライドすることで初期化処理を追加することができます。 インスタンス変数などの初期値や、システムに対する登録の問い合わせ処理、クラスが動作するために必要な事前データの読み込みなどは、この場で行うと良いでしょう。 ただし、init メソッドをオーバーライドする場合は Object クラスの init メソッドを呼び出すことを忘れないでください。
#import <stdio.h> #import <objc/Object.h> @interface Point : Object { int x, y; } - (id)init; - (int)getX; - (int)getY; @end @implementation Point - (id)init { [super init]; x = y = 0; printf("init method\n"); return self; } - (int)getX { return x; } - (int)getY { return y; } @end int main() { id pt = [Point new]; printf("X=%d, Y=%d\n" , [pt getX] , [pt getY]); return 0; }
このプログラムは、座標を表す X と Y の値を提供する Point クラスを実現しています。 Point クラスは Object ルートクラスを継承し、init メソッドをオーバーライドしています。 そのため、new メソッドを用いてインスタンスを生成した場合、インスタンス生成後に init メソッドが呼び出されます。
このプログラムの init メソッドでは、最初に親クラスを適切に初期化する必要があるので super に対して init メッセージを送信しています。 親クラスの init メソッドが実行されれば、この時点で親クラスのすべての機能が安全に利用できることが保障されます。 次に、インスタンス変数 x と y を 0 で初期化し、イニシャライザが呼び出されていることを視覚的に確認するために、printf() 関数で標準出力に文字列を出力しています。
init メソッドは、インスタンスが生成された直後に初期化のために呼び出される統一的な初期化用メソッドですが、引数を受け取ることができません。 サブクラスが、サブクラスで新しく追加された変数を初期化するための値を引数から取得することができれば便利です。 そのためには、サブクラスで新しいイニシャライザを宣言しなければなりません。 このような、Object ルートクラスの init メソッドとは異なるイニシャライザを指定イニシャライザと呼びます。
指定イニシャライザは、習慣的に initWith から開始する引数を受けるメソッドとなります。 サブクラスでスーパークラスの指定イニシャライザをオーバーライドした場合、必ずスーパークラスの指定イニシャライザを呼び出す必要があります。 それ以外は、習慣的な設計の問題となるでしょう。
#import <stdio.h> #import <objc/Object.h> @interface Point : Object { int x, y; } - (id)init; - (id)initWithPoint:(int)x int:(int)y; - (int)getX; - (int)getY; @end @implementation Point - (id)init { [super init]; return [self initWithPoint:0 int:0]; } - (id)initWithPoint:(int)x int:(int)y { self->x = x; self->y = y; return self; } - (int)getX { return x; } - (int)getY { return y; } @end int main() { id pt1 = [Point new]; id pt2 = [[Point alloc] initWithPoint:400 int:300]; printf("pt1.X=%d, pt1.Y=%d\n" , [pt1 getX] , [pt1 getY]); printf("pt2.X=%d, pt2.Y=%d\n" , [pt2 getX] , [pt2 getY]); return 0; }
このプログラムの Point クラスでは、指定イニシャライザ initWithPoint メソッドを定義しています。 initWithPoint は自動的に呼び出されるメソッドではないので、明示的に呼び出さなければなりません。 通常、init メソッドや指定イニシャライザは、最も引数を多く受け取るイニシャライザを順に呼び出す形が最も安定した設計となるでしょう。