実は、Objective-Cでプロパティを宣言すると、暗黙的にインスタンス変数が自動生成されます。
@property NSString *name; // 裏では「NSString *_name;」が宣言されている
これにより、クラス内部からプロパティの値を参照する際には、self.name
というアクセサ経由の方法に加え_name
やself->_name
によるインスタンス変数への直接アクセスが可能になります。ダークサイドプログラマ的には非常にオススメのテクニックです。
- (void)print {
NSLog(@"%@", self.name);
NSLog(@"%@", _name); // こちらのほうが書きやすく、しかも高速
}
メリット
このテクニックにより、値の呼び出し速度が高速になり、またデバッガー利用時には値の内容がデバッグコンソール画面の一覧に表示されるようになるという利点が生まれます。
ちなみにアクセス速度に関しては、構造体ポインタのメンバー変数へのアクセスと同等になります。ネイティブC並の処理効率が求められるケースで有効なテクニックとなります。
デメリット
ただし、インスタンス変数に直アクセスするということは、ゲッターメソッドの利点を捨てるという事にもなりますので注意が必要です。プロパティの遅延初期化やメソッドの結果を動的に制御する必要が出てきた際に、直アクセスを行っていた箇所の修正が必要になる場合もあります。
ダークサイ度:★☆☆☆☆
なお、本テクニックは実際のiOS開発現場でもそれなりに知られており、パフォーマンス・チューニング時のテクニックとしても有効ですので、ダークサイ度は星1つとします。
外部からアクセスする場合
あまりオススメしませんが、可能です。
_name
変数はデフォルトでプライベート扱いとなっていますので、外部からアクセス出来るようにするためには@public
によるアクセス制御を行う必要があります。また、インスタンス変数へのアクセスにはアロー演算子->
を用います。これは構造体ポインタへのメンバーアクセスと同じ方法です。
@interface Shop : NSObject {
@public NSString *_name;
}
@property (nonatomic) NSString *name;
@end
Shop *shop = Shop.new;
shop->_name = @"DAISO"; // インスタンス変数の参照が可能
注意
shop変数がnilの場合、shop.name
によるプロパティー呼び出しは無効化されますが、shop->_name
によるメンバアクセスはクラッシュします。
ポインタからのメンバアクセスは、メモリのアドレスを直接参照することになるため、nilに対するポインタアクセスは意図せぬアドレスを参照することになり、クラッシュしてしまうのです(いわゆるヌルポのような現象)。プロパティー呼び出しの場合はランタイム側でnilチェックが行われるためそのような問題は起きません。
ちなみにObjective-Cのオブジェクトは、実はC言語の構造体で出来ています。
Objective-C「私は80%のC言語と20%の愛で出来ています」
私「へ、へぇ・・・」