プロパティの話を続けよう。今回は、プロパティを宣言するための文法を調べてみる。
プロパティの目的
まず、プロパティの目的を明確にしておこう。プロパティとは、「クラスにアクセッサメソッドを簡単に付加する」ために導入されたものだ。
以前であれば、クラスが持つインスタンス変数に安全にアクセスさせるには、アクセッサメソッドを手動で追加する必要があった。これを自動化してやろう、というのがプロパティの目的だ。
ここで注意しておきたいのは、プロパティを使うとそのアクセスではメソッド呼び出しが発生する、というところだろう。前回、Objective-Cではほぼ自由にインスタンス変数にアクセスできるということを紹介した。プロパティではこれらの手段は使わずに、適切なメソッドを提供することになる。
この手法の利点は、安全であるということだ。とくにObjective-C特有の、retainやreleaseを伴ったオブジェクト管理の問題も同時に処理させることができる(ガベージコレクションを使わない場合)。欠点は、メソッドの呼び出しが発生するので、どうしてもパフォーマンス的に不利になることだろう。これらの利点と欠点を把握した上で、プロパティを使うかどうか決定してほしい。
@interface部でのプロパティ
では、プロパティを実際に使ってみよう。あるクラスにプロパティを追加する場合、@interface部と@implementation部の両方に手を加える必要がある。
まず、@interfaceでの宣言だ。例として、MyObjectというクラスに、NSString型のnameというプロパティを追加してみる。
リスト1
@interface MyObject : NSObject
{
}
@property (retain) NSString* name;
@end
「@property」で始まる行が、プロパティの宣言だ。@propertyの後に、括弧に入れた属性が続き、型名、プロパティ名で宣言する。
@property (属性) 型名 プロパティ名;
属性は、このプロパティのためのアクセッサメソッドの挙動を決定するものだ。これについては、次回以降の連載で詳しく説明しよう。
このままでも問題はないのだが、典型的な例では、プロパティは対応するインスタンス変数のために追加されることになる。そこで、このプロパティを使ってアクセスすることになるインスタンス変数も追加しておこう。
リスト2
@interface MyObject : NSObject
{
// プロパティに対応するインスタンス変数
NSString* name;
}
@property (retain) NSString* name;
@end
ここではプロパティの名前とインスタンス変数の名前を同じにしておいた。こうしておくと、少し実装が楽になる。だが、別の名前であっても問題はない。
これで、プロパティの型と名前を宣言できた。
@implementation部でのプロパティ
次に、@implementation部だ。こちらでは、プロパティのアクセッサメソッドの実装について、宣言することになる。
プロパティの利点は、アクセッサメソッドを自動的に作成してくれることだ。この利点を最大に活かすには、@synthesizeを使う。次のように記述することになる。
リスト3
@implementation MyObject
@synthesize name;
@end
これで完了だ。@synthesize指示子にプロパティ名を指定すると、アクセッサメソッドを自動的に「合成」してくれる。同名のインスタンス変数であるnameのための、getterとsetterを追加してくれるのだ。
プロパティ名とインスタンス変数名が同名の場合、これだけでいい。名前が違う場合は、@synthesize文でインスタンス変数名を明示的に指定する必要がある。たとえば、インスタンス変数名が_nameであったとしよう。この場合は、次のようになる。
リスト4
@implementation MyObject
@synthesize name = _name;
@end
プロパティ名の後に、インスタンス変数名を指定してやればいい。これで、プロパティを任意のインスタンス変数と関連づけることができる。
また、自動的なメソッド合成機能を使わずに、手動でアクセッサを実装することもできる。この場合、nameとsetName:というメソッドを自分で書けばいい。
リスト5
@implementation MyObject
- (NSString*)name
{
...
}
- (void)setName:(NSString*)name
{
....
}
@end
仮に、@synthesizeを書かず、手動でのアクセッサメソッドも実装しなければ、コンパイル時に警告が出ることになる。
さらに、Objective-Cらしい話ではあるが、コンパイル時にはアクセッサメソッドが存在しないが、実行時に動的にそれらが追加される、という状況もありうるだろう。このようなケースに対応するために、@dynamicという指示子も用意されている。
リスト6
@implementation MyObject
@dynamic name;
@end
プロパティを@dynamicで宣言すると、アクセッサメソッドは作られないし、コンパイル時の実装チェックも起こらない。実行時になって初めて、対応するアクセッサメソッドがあるかどうかチェックされることになるだろう。
実際にソースコードを書いてみると、@synthesizeを使うと比較的丁寧にエラーや警告を出してくれる。対応するインスタンス変数のチェックや、属性が適切かどうかなどを判断してくれる。だが@dynamicを使うと、動的に確認することになるので、コンパイル時のチェックはほとんどすっ飛ばされることになる。これでは台なしのような気もするが、Objective-Cらしいと言えば、その通りだろう。
これがプロパティの宣言になる。次回は、プロパティへのアクセスを調べてみよう。