|
|
3. プロパティ
原題では Declared Properties となっています。直訳すると「宣言されたプロパティ」となりますが、ここでは簡単にプロパティと呼ぶことにします
プロパティとは第1回の「オブジェクト」で50行もかかっていたクラス定義と実装の記述のうち40行も占めていたアクサセメソッドの定義と実装の記述をプログラマが記述する事もなくコンパイラに合成させる手法で Objective-C 2.0 からの新機能です。
ここではこれを利用して徹底的に Book.h と Book.m を楽な形に書き換えたいと思います。
まず任意の場所に book3 というファルダを作っておいてください。今回作るすべてのファイルはこの中に保存していきます。
Book.h
前回16行かかっていた Book クラスの定義 (@interface 部) が6行ですみました。これは前回4行に分けて記述していたインスタンス変数の宣言を1行にまとめたことで3行減ったことも含んでいます。このことを除いても16行が9行に減ったことになります。
Book.m
今度は前回34行もかかっていた Book クラスの実装 (@implementation 部) がたった3行に激減しました。これは驚きでしょう。
説明
まずプロパティとは直接関係ありませんが Book.h の5行目で4つの NSString 型のインスタンス変数を1行にまとめて宣言しています。これは learn C でも説明していたとおり同型の変数はカンマ ,で区切ってまとめて宣言できます。問題はこのインスタンス変数の型が NSString のポインターであるというところです。
ポインターの宣言ではアスタリスク * の位置は
型名 * 変数名
型名* 変数名
型名 *変数名
型名*変数名
のように以上の4箇所のどこの位置に記述しても構いません。しかし今回のように1行で同型のポインター変数を複数宣言することを考えると3番目の 型名 *変数名 という記述が一番書きやすいかなと思います。
7行目では
@property(copy, readwrite) NSString *name, *address, *pphone, *email;
と、@property という指示子によって4つのインスタンス変数のアクサセメソッドの定義を自動で合成するように指示しています。ここで自動合成されるのはあくまでもアクセサメソッドの定義(宣言)部分であって実装部分ではないことははっきりと区別して理解しておいてください。
( ) の中には属性というものが指定できます。属性は readwrite がデフォルトです。そしてその他に readonly と copy という属性があります。アクセサメソッドの合成なので readwrite がデフォルトだというのは分かります。デフォルトということはこの ( ) を記述しなければ readwrite という属性でアクセサメソッドが合成されるという意味だろうとは思いますが、実際にはまだ試していません。readwrite 属性の場合でも一応記述されておくことをおすすめします。
このプロパティ(アクセサメソッドの自動合成)では NSString * 型のインスタンス変数の setter メソッドは
インスタンス変数 = [NSStringオブジェクト copy]
という形で合成されます。従って @property 宣言の属性の部分に (copy, readwrite) と copy 属性も記述しておく必要があります。もし copy 属性を記述しなければコンパイルエラーか実行時エラーになります。(どちらのエラーが出たのかすでに記憶しておりません。すみません -:)
なお copy 属性が必要になるのは NSString *型インスタンス変数の場合だけなのか、あるいは他の型のインスタンス変数にも必要になってくるのかはまだ検証していません。もしエラーがでるようでしたら copy 属性を加えて確かめてみるという手もあります。
@property の宣言では、同じ属性で同じデータ型のインスタンス変数であればこの Book.h の7行目のようにカンマ , で区切って1行で宣言できます。しかし同型のインスタンス変数の場合であっても属性が違えばそれは別の行で @property 宣言しなければなりません。
Book.m 4行目
@synthesize name, address, phone, email;
アクサセメソッドの実装の自動合成は上記のように非常にシンプルです。
アクセサメソッドを @property で指定した属性に従って自動合成したい場合は、その属性やインスタンス変数の型に関係なく @synthesize 指示子に続けてインスタンス変数名をカンマ , で区切って続けて記述するだけです。当然 @property でアクセッサメソッドの自動合成の宣言していないインスタンス変数をここに記述するとエラーになるのではないかと思います。
しかし第1回の
オブジェクトのカプセル化
でも述べているように本来メソッドの実装は自由であるべきです。そこでこの宣言されたプロパティでは @dynamic という指示子をデフォルトとしています。@dynamic 指示子で指定したインスタンス変数は、仮に @property でメソッドの定義(宣言)を自動合成していた場合でもその実装は自動合成されません。プログラマー自身が通常通り記述しておかなければなりません。
なお、@dynamic がデフォルトになっているということは、@synthesize で指定していないインスタンス変数についてはわざわざ @dynamic を付けることなく実装を書きはじめることができるということだと思いますが、この場合の検証はまだしていません。お時間があればぜひ確かめてみてください。
実行
さて新しく作った Book.h と Book.m を保存した book3 フォルダに 前回の book2 で作った loop.m をコピーします。
サンプルファイルは
book3.zip
からもダウンロードできます。
ダウンロードした book3 フォルダをホームフォルダにでも置いていただいてターミナルを起動します。もちろんすべてを手書きしてもらうことにこしたことはありません。
cd book3
などで loop.m Book.h Book.m のあるフォルダに移動します。
まずはこのままリファレンスカウンタ方式でコンパイルしてみます。
gcc loop.m Book.m -o book -framework Foundation -wall
実行は
./book
前回第2回では宣言されたプロパティを採用した場合にはガベージコレクションと一緒に使わなければエラーが出る場合があると書きましたが、このレベルではまったく正常にコンパイルと実行ができるみたいです。
次に loop.m の34行目の
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
と、56行目の
[pool release];
を削除してガベージコレクション方式でコンパイルしてみます。
今後、この learn ObjC 2.0 の中でリファレンスカウンタ方式に戻ることはないと思います。この2行はコメント化ではなくて削除してもらって構いません。もし必要になってもわずか2行ですからいつでも書き加えることができると思います。コンパイルコマンドは、
gcc loop.m Book.m -o book -framework Foundation -fobjc-gc-only -wall
です。実行は同じく
./book
です。
この場合もまったく問題なくコンパイルと実行ができるみたいです
次回にむけての準備
簡単でしたが、これでプロパティの説明は終ります。
次回の第4回ではコレクション (集合) の説明をしたいと思います。コレクションとはデータをまとまて管理する仕組みのことです。Book オブジェクトは一人分の情報を管理しますが、この Book オブジェクトをアドレス帳のように1つの(一冊の)データにまとめるものがコレクションです。
コレクションクラスの詳しい説明は次回にまわしますが Objective-C 2.0 の三大新機能のうちの最後の Fast Enumeration 高速列挙 はこのコレクションの各データを言葉通りに高速に列挙する仕組みのことです。どのくらい高速になったのかをわずか数個のデータ量で実感することは本来あり得ないとは思いますが、使ってみると気のせいか早くなっているような気がします :-)
そういうわけなので次回第4回では高速列挙の前にコレクションの説明をして実際にデータをコレクション化します。そして第5回では ファイル入出力 (データ保存とデータ読み込み) の説明をしたいと思います。
プログラムには継続性があるものとないものがあると思います。すなわち1回1回だけで結果が得られてそれだけで良いものと、プログラムを起動するたびに前回までの結果(データ)から続きを始めたいものがあります。データの保存と読み込みができなければこのように継続性のあるプログラムはできません。高速列挙も大事なことですがプログラムの条件としての優先順位からすればこのファイル入出力のほうが大事なことでしょう。
そして第6回で高速列挙の説明をしてこの learn ObjC 2.0 を終りたいと思っていますが、第4回のコレクションの時に 高速列挙 の説明もできるような気もします。高速列挙については本当にそれほど難しい説明は何もありません。新しいループ文を説明するぐらいの行数で説明が終るのではないかと思います
では次回に向けて main 関数の中のメインループを少し改良しておきたいと思います。loop.m をもとにして今度は main.m というファイル名で改良したコードを書いたファイルを book3 フォルダに保存します。
main.m
この main.m ファイルは
book3.zip
の中にも含まれています。
loop.m から変更された箇所は
・5行目で exist (存在するという意味) の BOOL 型グローバル変数が宣言されていること
・enterBook 関数をやめて showBook と addBook という Book のデータを表示する関数と、入力する関数を2つにわけたこと (その2つの関数のプロトタイプが7行目と8行目に記述されています)
・19行目のようにユーザーの選択肢が「表示=1 入力=5 終了=9」の3つに増えたこと
・38行目で exist 変数が NO の場合には「何もデータが入力されていません.」と表示されてプログラムが最初の「表示=1 入力=5 終了=9」に戻ること
・66行目で exist 変数に YES が代入されるので、addBook 関数が1度でも呼び出された場合には仮に入力されたデータがすべて空文字でも次回からは showBook 関数が表示に応えること
などです。
showBook 関数の中では cStringUsingEncoding: メソッドの引数を45行目以降では簡易的に整数の4で済ませています。本来は NSUTF8StringEncoding と正しい引数を記述することを強く推奨いたします。
コンパイルコマンドは
gcc main.m Book.m -o book -framework Foundation -fobjc-gc-only -wall
実行は
./book
です。
実行画面は下記のようになります
お疲れさまでした。これで learn ObjC 2.0 の第3回は終ります。
次回第4回では Book オブジェクトをコレクション化して、選択できるコマンドも「一覧表示、指定表示、登録、削除、終了」の5つに増やしたいと思います。
|
|
|