25899 visitors

Objective-C 2.0 簡易版

no 0  改訂履歴
no 1  オブジェクト
no 2  ガベージコレクト
no 3  プロパティ
no 4  コレクション


このサイトについて
contact me
home

Programming
C 言語
learn C
Objective-C 2.0 言語
learn ObjC
Objective-C 2.0 言語 簡易版
learn ObjC  Lite
Cocoa GUI アプリケーション
Cocoa GUI App
メモリ管理検証  new
Memory Management Test

Other
参考図書・グッズ
Favorites
ソフトウエア
Software  soon

Legacy
古い記事
Objective-C Primer
 宣言されたプロパティ (Declared Properties) も Objective-C 2.0 から採用された新しい機能です。従ってガベージコレクションと同様に Mac OS X 10.5 Leopard 以降でしか使えません。ここでは簡略的にプロパティと呼ぶことにします。

 まず第2回の lite2 フォルダをそのままコピーして lite3 というフォルダを作ってください。この作業はファインダー上で行って頂いても結構ですし、ターミナルで lite ディレクトリまで移動して
cp -r lite2 lite3
とコマンドすることでもコピーできます。
コピーが出来ましたら新しい lite3 フォルダの中の新しい ITModel.h と ITModel.m を次にように書き換えます。

ITModel.h


ITModel.m


 では早速コンパイルしてみます。
ターミナル上で lite3 ディレクトリに移動してください。ホームフォルダからは
cd lite/lite3
で移動できます。pwd とコマンドすると現在の位置が分かります。コンパイルコマンドは前回と同じ
gcc main.m ITModel.m -o intro -framework Foundation -fobjc-gc -wall
です。実行も同じく
./intro
です。
正常に動作したことだろうと思います。

なお、このサンプルプログラムは lite3.zip からダウンロードすることもできます。


宣言されたプロパティ

 プロパティ (宣言されたプロパティ) はインスタンス変数の値を設定するメソッド (これを setter と呼びます) とインスタンス変数の値を取得するメソッド (これを getter と呼びます) の宣言(定義)と実装を自動的に合成する機能です。なお setter と getter を合せて accesser (アクセサ) と呼びます。
 今回の ITModel オブジェクトのインスタンス変数は1つだけでした。しかし通常は複数のインスタンス変数になるほうが多いと思います。そして Objective-C ではそのインスタンス変数に直接アクセスできるのは同じオブジェクト内からだけです。違うオブジェクトからはアクセサメソッド(アクセサ)を使ってアクセスします。これは一見わざと難しくしているだけのように思いますがこのようにしたのにはそれなりの理由があります。その理由については後ほど「オブジェクトのカプセル化」の所で簡単な説明をいたしますが、このようなアクセサメソッドのコードをわざわざ記述しなければならないのは確かに面倒です。そこで Objective-C 2.0 ではこのアクセサメソッドのコードを自動的に合成することができるようになりました。


ヘッダファイルでのアクセサメソッド宣言の自動合成

 ITModel.h 7行目 @property(copy, readwrite)NSString *string;
この記述で string インスタンス変数のセッターとゲッターの2つのアクセサメソッドをプログラムが自動合成してくれるようになります。記述する場所はインスタンス変数の宣言の終った } 以降から最後の @end までの間のどこでも構いません。分解して説明すると
 @property ( 属性 ) インスタンス変数の型 インスタンス変数名 ;
となります。まず @property で始めます。次に ( ) の中に属性というものを書きます。属性には、
・readwrite
・readonly
・copy
の3つがあります。
デフォルトは readwrite ですが、NSString 型のインスタンス変数のセッターメソッドを自動合成する場合には7行目のように copy 属性も必要となります。なぜなら NSString 型のインスタンス変数に対して自動合成されるセッターメソッドは
 インスタンス変数 = [ 引数の NSString 文字列 copy ] ; というコードを合成するからです。このほかにも copy 属性を必要とするセッターメソッドは存在すると思います。もしうまく動作しないようでしたら copy 属性を付加して試してみるのも手かもしれません。属性は7行目のようにカンマ ( , ) で区切って複数指定できます。次にインスタンス変数の型とインスタンス変数名が続きます。同じ属性で同じ型のインスタンス変数の場合は、 @property ( 属性 ) インスタンス変数の型 インスタンス変数A, インスタンス変数B, インスタンス変数C ; とカンマ ( , ) で区切って複数の変数を同時に指定することもできます。


実装ファイルでのアクセサメソッド実装の自動合成

 ITModel.m 10行目 @sythesize string;
@synthesize インスタンス変数名 ; と記述することによって @property の属性に従ってインスタンス変数に対するアクサセメソッドの実装が自動合成されます。( readwrite ) 属性や ( copy, readwrite ) 属性を指定している場合にはセッターとゲッターの両方のメソッドの実装が自動合成されます。@synthesize のあとには属性やインスタンス変数の型に関係なく @synthesize インスタンス変数A, インスタンス変数B, インスタンス変数C ; とカンマ ( , ) で区切って複数のインスタンス変数を記述することが出来ます。

 実装ファイルでの「宣言されたプロパティ」の指示子にはもう1つ @dynamic というものがあります。
@dynamic 指示子の後に指定されたインスタンス変数に対しては、たとえヘッダファイルの @property でアクサセメソッドの宣言を自動合成していても、その実装は自動合成されません。アクセサメソッドの宣言はすべてのインスタンス変数においてほぼ同じ形をしています。従って自動合成するのに向いています。しかしアクセサメソッドの実装はクラスによって相当異なるものになるであろうと思われます。@synthesize で実装を自動合成してしまうとそのコードは型通りになってしまってそのクラスにはそぐわないものになる可能性が多いにあります。従って実装ファイル (クラス名.m) での宣言されたプロパティでは @dynamic がデフォルトとなっています。


オブジェクトのカプセル化

 Objective-C では他のオブジェクトのインスタンス変数には直接アクセスできません。必ずアクセサメソッドを通してアクセスします。このようにオブジェクトの内部データを隠す (あるいは守るという表現でも良いかもしれません) 構造になっていることをオブジェクトのカプセル化もしくは隠蔽化と呼びます。

 Student (生徒) というクラスがあったとします。この Student クラスから生徒一人一人のインスタンスを作ったとします。そして各インスタンス (オブジェクト) には scoreOfEnglish (英語の点数) というインスタンス変数があったとします。テストの点数は普通 0点 〜 100 点までに決まっています。しかしこの点数を入力するのに何の制限もなければ -20 点や 120 点という点数も入力できてしまいます。そこで点数を入力する場合には必ずアクセサメソッドを通して入力することにします。

 setScoreOfEnglish: アクセサメソッドでは引数として受け取った点数の値が 0 〜 100 であればそのままインスタンス変数 scoreOfEnglish に代入します。しかし 101 以上であればその値を 100 に直して、また -1 以下であればその値を 0 に直してから代入するようにします。こうすることによって scoreOfEnglish のデータは常に正常な範囲のものに保たれます。

 このような理由でインスタンス変数にアクセスするにはアクセサメソッドを通さなければならないようになっているのですが、オブジェクトのカプセル化にはもう1つ意味があります。
 さきほどは不正な数値が入力された場合には自動的に正常範囲内の数値に直してインスタンス変数に代入しましたが、本当のところはそれが正しい点数なのかどうかは分かりません。そこで setScoreOfEnglish: メソッドを引数の値が 0 〜 100 以外であれば「入力された値は点数の範囲外です。正しい点数を入力してください。」とユーザーに再入力を求めるように換えることもできます。

 以上のように考えるとオブジェクトのカプセル化はそのデータ (インスタンス変数) に対しても、そしてまたメソッドに対しても有効です。ここでの ITModel というクラスは私達が作りましたからその内容 (メソッドの実装内容) が分かっています。しかしその他の最初から用意されているクラスの実装は実際のところ分かりません。また分かる必要もないわけです。私達 (プログラマ) が知っておかなければならないのはそのクラスにどのようなメソッドが用意されていて、それをどう使えば良いかということだけです。そしてメソッド名と使い方さえ分かっていれば、そのメソッドを実行した結果が自動的に 0 〜 100 の範囲内の数値に換えるものであるのか、あるいは「再入力してください」と促されるものなのかは、そのクラスを作った人の自由であり、またその人の責任範囲だとも言えます。このように考えていくと宣言されたプロパティで実装ファイルの指示子のデフォルトが @dynamic になっているのも当然のことだと言えます。

 またヘッダファイルはそのクラスにどのようなメソッドがあるのかを私たちが知るためにもテキストファイルの形で残してあるのだとも言えます。


簡単なファイル入出力


 プログラムで作られるデータは一旦メモリー上に格納されます。そしてプログラムを終了すればこのメモリー上のデータもなくなります。いまのままでは intro プログラムの自己紹介文を編集しても次に起動した時にはまた "Hello world !" に戻ってしまいます。編集したデータをプログラムの終了後も保持するためにはメモリー上に格納されているデーターをファイルという形でハードディスクなどに保存しなければなりません。このことをファイルの入出力と呼びますが、ファイル入出力には
1.人間の読める形のテキストデータをテキストデータのまま保存・読み込みをする
2.人間の読める形のテキストデータを plist というCocoa独自の形式で保存・読み込みをする
3.人間の読める形のデキストデータや人間には読めないバイナリデータをバイナリデータとして保存・読み込みをする
 の3つの方法がありますが、今回は一番簡単な1番の方法で編集した自己紹介文をファイルに書き出して保存し、次にプログラムを起動した時にはそのファイルを読み込むようにします

では ITModel.h ITModel.m main.m を次のように書き換えてください。

ITModel.h


7行目
string インスタンス変数のデータをファイルに書き出すための writeFile という新しいメソッドの宣言を書き加えています。

ITModel.m


16行目〜19行目
writeFile メソッドを実装しています。
writeToFile: atomically: encoding: error: メソッドはレシーバーの文字列を writeToFile: の引数で指定した場所にファイル名で指定したファイルとして書き出します。今回は起動ディスクの第一階層にある「ライブラリ」フォルダの中の「Preferences」フォルダに ZZIntro.txt という名前で書き出すことにしています。「ZZIntro.txt」としたのは Preferences フォルダをリスト表示にした場合に一番下に表示され探すのが楽だからです。ラベル atomically: では引数に YES か NO を指定します。YES を指定するとレシーバーの文字列を一旦一時ファイルに書き出してから指定されたファイルに書き出します。ラベル encoding: では使用する文字エンコーディングを指定します。文字エンコーディングについては後ほど説明します。ラベル error: ではエラーが起こった場合に返されるエラー表示を指定します。NULL を指定すると何もエラーは返されません。

 起動ディスクの「ライブラリ」フォルダではなく現在コンピューターを使用しているユーザーのホームフォルダの「ライブラリ」フォルダを指定する方法もあります。詳しくは learnObjC 第6回 ファイル入出力 をご覧になってください。

文字エンコーディング
 文字はコード番号 (数字) で管理されています。どの文字に何番を割り当てるかはについては複数のシステムがあります。このシステムのことをエンコーディングと呼びます。今回はラベル encoding: で UTF-8 を表す NSUTF8StringEncoding を指定しています。

8行目
まずはじめに NSString のクラスメソッド stringWithContentsOfFile: で指定された場所のファイルから、ラベル encoding: で指定した文字エンコーディングを使って文字列を読み込みます。今回も UTF-8 エンコーディングを指定しています。ラベル error: は今回も簡略的に NULL にしています。

9行目
指定したファイルがなかったり何かの事情で string に文字列が読み込まれなかった場合には string インスタンス変数は nill のままになっています。その場合には11行目で string に "Hello world ! " を代入します。

main.m


35行目
[ model writeFile ] ; で ITModel の writeFile メソッドを呼び出して (メッセージを送って) データを書き出します。このコードを書く場所としては他に34行目の前や switch 文の case 3: の中などがあります。

書き換えが終りましたらコンパイルをし直します。
gcc main.m ITModel.m -o intro -framework Foundation -fobjc-gc -wall
実行は
./intro
です。

 起動して何か新しい紹介文を入力します。そして終了させると、自動的に 起動ディスク/ライブラリ/Preferences に ZZIntro.txt というファイルが出来上がっています。このファイルはテキストデータなので Mac に付属している「テキストエディタ」などでも開いて中を見ることができます。
 次に intro プログラムを起動させ 挨拶=1 を選ぶと最後に入力した文字列が表示されます。また intro プログラムを終了させてから ZZIntro.txt を削除して起動させると 挨拶=1 は Hello world ! に戻ります。





お疲れさまでした。これで「learn ObjC  Lite」の第3回は終わります。
今後の予定ですが、今回の第3回で learn ObjC  Lite は終わりにするかもしれません。
理由としては、

1.今回の第3回までで Onbjective-C の入門編としての説明の大部分は終ったとも思える
2.NSDictionary を使っての高速列挙の説明を考えていましたが高速列挙は NSDitionary に未対応という噂がある
3.そろそろ Ruby を使った日記帳を作るか、あるいは Cocoa GUI アプリケーションの説明に進みたい

などです。


目次を表示 (先頭へ戻る) 前ページ   次ページ

This site is available in Safari and Leopard. © ttezu 2006 - 2008