Objective-C 宣言プロパティで利用できるオプション.
今日は、宣言プロパティで使用できる属性とその宣言の仕方を見ていきます。
プロパティの属性とは
例えば、前々回の投稿「宣言プロパティとは」で出てきた、starSignというプロパティを見てみると、これは外部から読み取り専用にしたいインスタンス変数のプロパティでした。
アクセサでこれを実現する場合は、セッタメソッドを書かず、ゲッタだけを書くことで実現できました。
これを宣言プロパティで実現しようとすると、普通に宣言したのでは、ゲッタもセッタも提供されてしまいます。
そこでこのような場合は「readonly」という属性オプション使ってやります。これを設定することで、ゲッタだけを提供するプロパティとして定義できます。
このように読み書きの方法や、値の設定の仕方をある程度カスタマイズできる用にいくつかのオプションが用意されています。
その一覧を以下の表にまとめました。
合計11のオプションが用意されています。そして、これらは組み合わせて指定可能です。しかし、同じタイプに分類されているオプションは同時に組み合わせて指定することはできません(セッタ、ゲッタ名指定は一緒にできます)。
例えば、”readonly”と”readwrite”を一緒に指定すると矛盾が生まれるので、指定できません。
指定は以下のように行います。
@property (readonly) int starSign; @property (readwrite, nonatomic) NSString* name; @property (setter=setGender:) BOOL man
2行目が複数のオプションを指定している例です。このようにコンマで区切って指定します。
3行目はセッタ名を独自の名前で指定しています。こうすることで、独自で作ったメソッドsetGenderをセッタとして指定できます。アクセスには「 obj.man = YES; 」 のようにドット演算子が使えますが、呼ばれるのはsetGenderメソッドです。
値の設定方法を指定するオプション
少々ややこしいのが値の持ち方を指定するオプションタイプです。6種類のオプションが選べるようになっています。
それぞれの挙動は、そのプロパティがオブジェクトのためのものかどうか、またメモリ管理をどの方式で書いているかどうかに依存します。
大まかな挙動の違いを以下の表にまとめてみました。
まずテーブルの要素について説明します。
オブジェクト型とはいわいるid型として扱えるものです。NSString*やNSDictionary*などアスタリスク「*」が付いているものは大体そうです。逆にオブジェクト型でないものは、intやfloatなど型です。
次に、ARCなどと書いてある部分がメモリ管理方式の種類です。従来のメモリ管理方式(手動で行うもの)は「デフォルト」と書いてある部分です。”release”や”autorelease”などが使われているプログラムは、このデフォルトの方式で書かれたプログラムだと考えていいです。
ARCはAutomatic Reference Countingの略で、自動でメモリ管理をしてくれる方式のものです。同様にガベージコレクションも、自動的に不要になったメモリ領域を開放してくれるシステムを用いてプログラムが書かれているタイプのものです。
まだメモリ管理については解説していないので、あまり深く考えなくてもいいのですが、このような方法があるという事だけでも知っておいてください。
オブジェクト型でないものでももちろん、メモリ管理方式の違いで分けれますが、ここではすべて同じ働きをするので、統合して書いてあります。
とにかくこれらの組み合わせによって、提供される機能が違います。
ここでは
- プロパティがオブジェクト以外の場合
- オブジェクトで、デフォルトのメモリ管理方式の場合
について解説します。
1). プロパティがオブジェクト以外の場合
プロパティの型がintやfloatなどid型でない場合、その変数には値を代入することしかできませんので、「代入」という機能しかありません。
assignを指定すれば代入を明示することができますが、何も指定しなくてもデフォルトがassignになっていますので、代入機能が提供されます。具体的には以下の様な処理が行われます。
/* ゲッタの擬似処理 */ -(型) 型名 { return 型名 } /* セッタの擬似処理 */ -(void) set型名: (型)obj { 型名 = obj; }
典型的なゲッタ、セッタのメソッドです。
また、他のオプションを指定するとエラーになります。
2). プロパティがオブジェクトで、デフォルトのメモリ管理方式の場合
オブジェクトの場合はいずれかのオプションを指定しなければなりません。assignを指定すると、オブジェクト型でない場合のassignと同じように代入が行われます。
retainは保持という意味で、そのオブジェクトが確保しているメモリ領域を、他のメソッドが開放してしまわないように保持しておくものです。ゲッタはassignと変わりませんが、セッタが以下のような処理として扱われます。
-(void) set型名: (型)obj { if(型名 != obj) { [型名 release]; 型名 = [obj retain]; } }
「 [型名 release]; 」で現在保持しているプロパティの内容を開放しています。その後retainメソッドを用いてobjを保持するよう処理しています。
copyは複製を作るメソッドで、プロパティにセットしたオブジェクトの複製を作り、それを保持します。この場合のセッタは以下のような処理と同じです。
-(void) set型名: (型)obj { if(型名 != obj) { [型名 release]; 型名 = [obj copy]; } }
このように、値を設定する方法は、そのプロパティの型とメモリ管理方式によって決めなければなりません。
メモリ管理については、かなり長い話になるので、あとで詳しくやります。
アトミック性
さてもう一度初めの表に戻りましょう。最後の行にある属性「アトミック性」についてです。
まず、並列プログラミングについて少し説明すると、複数のスレッドが同時に動作するように組まれたプログラムのことです。
アトミックとは、並列プログラミングの際のメソッドの状態を表す言葉で、同時にそのメソッドが実行されることはないということを示します。
一方、nonatomicとはその逆で、そのメソッドがプログラム中に同時に実行される可能性があるという事を示す言葉です。
通常、nonatomicを指定しない場合だと、そのプロパティに複数の処理が同時にアクセスしても”lock”、”unlock”機能が働き一つずつしか処理されません。これによって、アクセサメソッドの安全性を高めています。
すなわち、このプロパティオプションでnonatomicを指定することは、「このアクセサメソッドは同時に実行されることはないから、ロックしなくていい」ということをコンパイラに示していることになります。
lock機能を付け加えたアクセスでは、処理が多少ながらも遅くなるので、絶対に安全だというプロパティにはnonatomicをつけることで、処理速度を若干ながら早くすることができます。
以上が、宣言プロパティの定義で指定できるオプションの解説です。
まだ、メモリ管理や並列プログラミングについて解説していないので、難しい部分が多かったかと思いますが、逐次、補足して行きたいと思います。
大体Objective-Cの概念もわかってきたと思うので、次回はそろそろcocos2dの方に戻って行きたいなと思ってます。