ホーム > Objective-C > Objective-C 2.0 のプロパティ隠蔽

Objective-C 2.0 のプロパティ隠蔽

2010 年 05 月 21 日 コメントをどうぞ コメント

Objective-C 2.0 から導入されたプロパティについて誤解があったのでメモ。

プロパティは、getter と setter の自動生成と値の保持方法を明確化するための仕組みです。これが、C++ や Java で不必要にメンバが Public のような、オブジェクト指向のカプセル化をなしくずしにしているように思えて気になっていたのですが、無知からくる勘違いでした。

プロパティの基本。

クラスのメンバ変数に関する外部からのアクセスは、従来であれば

Hoge.h   
  1. @interface Hoge {
  2.     NSString* name;
  3.     NSInteger value;
  4. }
  5. - (NSString*)name;
  6. - (void)setName:(NSString*)newName;
  7. - (NSInteger)value;
  8. - (void)setValue:(NSInteger)newValue;
  9. @end
Hoge.m   
  1. @implementation Hoge
  2.  
  3. - (void)dealloc {
  4.     [name release];
  5. }
  6.  
  7. - (NSString*)name {
  8.     return name;
  9. }
  10.  
  11. - (void)setName:(NSString*)newName {
  12.     if (name != newName) {
  13.         [name release];
  14.         name = [newName retain];
  15.     }
  16. }
  17.  
  18. - (NSInteger)value {
  19.     return value;
  20. }
  21.  
  22. - (void)setValue:(NSInteger)newValue {
  23.     value = newValue;
  24. }
  25.  
  26. @end

と記述しましたが、プロパティを使えば同じことを

Hoge.h   
  1. @interface Hoge {
  2.     NSString* name;
  3.     NSInteger value;
  4. }
  5. @property (retain) NSString* name;
  6. @property (assign) NSInteger value;
  7. @end
Hoge.m   
  1. @implementation Hoge
  2. @synthesize name;
  3. @synthesize value;
  4.  
  5. - (void)dealloc {
  6.     [name release];
  7. }
  8.  
  9. @end

だけで書けてしまい、さらに利用側からは getter/setter メソッドを呼ぶ代わりにピリオド演算子(.)でアクセスできてしまうという素敵文法。可読性や安全性も向上します。

機能的には C# のプロパティとほぼ同じものです。C++ や Java にはこういった言語機能はありません。

Objective-C 2.0 は Mac OS X 10.5 と共に登場したので、私自身は 10.4 をサポートする IP Messenger では利用出来ず、iPhone アプリの開発で初めて触れました。iPhone の開発チュートリアルや開発本では、どのクラスでもほぼすべてのメンバ変数を readwrite プロパティとして宣言するように記載されており、クラス内部でプロパティアクセスするときにはプロパティの恩恵が受けられるのでいいのですが、そのメンバを外部から書き換えることが出来てしまうのは問題です。これがどうもメンバ変数を不必要に Public 宣言してしまうようで違和感がありました。

プロパティの再定義。

プロパティを、外部からは見えないがクラス内部には存在する、外部からは readonly だがクラス内部では readwrite という形で定義できれば、問題は解決します。そういったことができないなんて Objective-C らしくないし、普通に考えておかしいな、と思っていたら、出来ました。

Hoge.h   
  1. @interface Hoge {
  2.     NSString* name;
  3.     NSInteger value;
  4. }
  5. @property (readonly, retain) NSString* name;
  6. @property (readonly, assign) NSInteger value;
  7. @end
Hoge.m   
  1. @interface Hoge()
  2. @property (readwrite, retain) NSString* name;
  3. @property (readwrite, assign) NSInteger value;
  4. @end
  5.  
  6. @implementation Hoge
  7. @synthesize name;
  8. @synthesize value;
  9.  
  10. - (void)dealloc {
  11.     [name release];
  12. }
  13.  
  14. @end

こうすると、Hoge.h を読み込んだソースでは namevalue も readonly 属性のプロパティと解釈されて、値は参照できますが代入するとコンパイルエラーになります。ただ、Hoge.m の中では readwrite のプロパティと解釈されて、代入も自在にできるようになります。クラス内部では当然代入する機会がありますので、安全で可読性の高いソースになります。

従来からの Objective-C にもある、カテゴリ拡張によりローカルメソッドを定義するやり方に似ていますが、クラス拡張(無名のカテゴリ拡張)によりプロパティの属性を変更しています。クラス拡張は Objective-C 2.0 の新機能です(いままでは、よく Private とか Local といった名前をつけてクラスローカルメソッドをカテゴリ拡張していました)。
プロパティの属性変更は、クラス拡張の他にプロトコルとサブクラスでも可能です。

詳しくは、以下の URL のドキュメントの中にある「Objective-C 2.0 プログラミング言語」を参照ください。他にもプロパティ宣言に関する仕様などが説明されています。

http://developer.apple.com/jp/documentation/japanese.html

萩原本も Objective-C 2.0 の改訂版買ってちゃんと読まなきゃ(改訂前のは持ってるのですが)。

アマゾンのサーバでエラーが起こっているかもしれません。
一度ページを再読み込みしてみてください。


カテゴリー: Objective-C タグ:
  1. コメントはまだありません。
  1. トラックバックはまだありません。