前回の記事で、Personクラスに名前と年齢を表すインスタンス変数(メンバー変数)とそれらを取得・設定するメソッドを定義しましたね。
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
// 名前
NSString *_name;
// 年齢
NSInteger _age;
}
// 名前を取得する
- (NSString *)name;
// 名前を設定する
- (void)setName:(NSString *)name;
// 年齢を取得する
- (NSInteger)age;
// 年齢を設定する
- (void)setAge:(NSInteger)age;
@end
#import "Person.h"
@implementation Person
// 名前を取得する
- (NSString *)name
{
return _name;
}
// 名前を設定する
- (void)setName:(NSString *)name
{
_name = name;
}
// 年齢を取得する
- (NSInteger)age
{
return _age;
}
// 年齢を設定する
- (void)setAge:(NSInteger)age
{
_age = age;
}
@end
このとき、インスタンス変数から値を取り出して返すメソッドを「ゲッター(Getter)」、インスタンス変数に値を代入するメソッドを「セッター(Setter)」と言います。そして、これらのメソッドをまとめて「アクセサー(メソッド)」とも呼びます。
例えば、「- (NSString *)name」メソッドはインスタンス変数「_name」のゲッター、「- (void)setName:(NSString *)」メソッドはインスタンス変数「_name」のセッターとなります。
このように、インスタンス変数には、アクセサーを介して値の取得や設定を行うことが一般的ですが、これらの定義を毎回書くのは面倒ですよね。
Objective-Cでは、インスタンス変数とアクセサーをまとめて自動生成する機能があります。
それがプロパティ(@property)です。
プロパティを使用するにはプロパティを宣言する必要があります。プロパティの宣言は以下のように書きます。
@property (オプション) 型 プロパティ名;
@interfaceと@endと異なり、文末に「;(セミコロン)」が必要です。オプションについては後で解説しますので、今は気にしないでください。
プロパティを利用して、Person.hとPerson.mを書き換えると、以下のようになります。
#import <Foundation/Foundation.h> @interface Person : NSObject // 名前 @property (nonatomic) NSString *name; // 年齢 @property (nonatomic) NSInteger age; @end
#import "Person.h" @implementation Person @end
だいぶスッキリしましたね。特に実装ファイルはほとんど何もなくなりました。
このとき、インスタンス変数はプロパティ名の頭に「アンダースコア(_)」が付与されたものが生成されます(例:「_name」「_age」)。
ゲッターメソッドは、メソッド名にそのままプロパティ名が入ります(例:「- (NSString *)name」「- (NSInteger)age」)。
セッターメソッドは、プロパティ名の頭にsetが付与され、プロパティ名の頭文字が大文字になります(例:「- (void)setName:」「- (void)setAge:」)。
定義したプロパティへアクセスするには、ドット(.)演算子を使用します。ViewController.m を以下のように変更して実行してみましょう。
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Personクラスのインスタンスを生成する
Person *aPerson = [Person new];
// nameに値を設定する
aPerson.name = @"山田太郎";
// nameから値を取得する
NSString *aName = aPerson.name;
NSLog(@"aName : %@", aName);
}
@end
非常に直感的に書けるようになりましたね。
また、セッターとゲッターは自分で定義することもできます。試しに、Person.m を以下のように変更してみましょう。
#import "Person.h"
@implementation Person
- (NSString *)name
{
return [NSString stringWithFormat:@"%@ 様", _name];
}
@end
これで実行すると、以下のように表示され、自分で定義したゲッターが実行されていることが分かります。また、内部的に「_name」という名前のインスタンス変数が生成されていることも確認できます。
後回しにしたプロパティのオプションについて解説します。プロパティで指定できるオプションには、以下のものがあります。
| 項目 | オプション | 意味 |
|---|---|---|
| 所有属性 | strong | オブジェクト型のプロパティで指定可能。オブジェクトを強い参照で保持する。勝手にメモリから解放されてしまわないようにする |
| weak | オブジェクト型のプロパティで指定可能。オブジェクトを弱い参照で保持する。メモリから解放されてしまう場合がある。その場合はnilが代入される。 | |
| copy | オブジェクト型のプロパティで指定可能。オブジェクトを強い参照で保持することはstrongと同様だが、保持するインスタンスは受け取ったインスタンスをコピーしたものになる | |
| assign | NSIntegerやBOOLなどプリミティブな型のプロパティに指定 | |
| スレッドセーフ | atomic | プロパティをスレッドセーフ(複数のスレッドが同時並行的に実行しても問題が発生しないこと)にする |
| nonatomic | スレッドセーフにしない | |
| アクセス制御 | readonly | 読み取り専用にする |
| readwrite | 読み書き可能にする | |
| アクセサー名の指定 | getter= | ゲッターの名前を自分で指定 |
| setter= | セッターの名前を自分で指定 | |
所有属性については、NSStringやNSArrayなどの既存のクラス、または独自に定義したクラスなどのオブジェクト型の場合はstrongが、それ以外の型の場合はassignがデフォルトで設定されるため、省略できます。
それ以外のオプションについては、スレッドセーフに関するオプションを省略した場合はatomicが、アクセス制御に関するオプションを省略した場合は、readwriteがデフォルトで設定されます。
そのため、先ほどPerson.hに記載した宣言は以下のものと同様です。
@property (strong, nonatomic, readwrite) NSString *name; @property (assign, nonatomic, readwrite) NSInteger age;
明示的にnonatomicを指定しているのは、atomicを指定してもパフォーマンスを悪化させるだけで、ほとんどの場合にメリットが無いためです。ですので、よほどのことが無い限りはnonatomicを指定しておきましょう。
Copyright © ITmedia, Inc. All Rights Reserved.