|
|
4. コレクション
Objective-C にはオブジェクトの集合を扱うためのクラスが6つ用意されています。これらのクラスのことをコレクションと呼びます。コレクションを大きく分けると次の3つになります。
NSArray
Array とは配列のことです。ただし C 言語の配列と違う点は NSArray では型の指定はできません。型としてはオブジェクト型の配列としてのみ扱われます。他の型の配列を使いたい場合はそのまま C 言語の配列を使えば良いということになります。
NSDictionary
Array (配列) は各要素 (オブジェクト) を Index (添え字) で識別します。添え字とは 0 から始まる続き番号のことです。それに対して Dictionary (辞書) は各要素 (オブジェクト) をキー (名前) で識別します。要素の事を値 (Value) と呼び一組の値とキーのペアをエントリーと呼びます。エントリーはいくつでも登録することができますが NSDicitonary も NSArray と同じく各要素およびキーはオブジェクトでなければなりません。キーには普通 NSString (文字列オブジェクト) が使われます。
NSSet
Set (集合) は番号やキーによって識別する必要のないオブジェクトの集合を取り扱います。
不変クラスと可変クラス
各コレクションクラスにはそれぞれのサブクラスとして NSMutableArray NSMutableDictionary NSMutableSet というクラスが用意されています。Mutable とは変更できるという意味になります。それぞれの親クラスは不変 (変更不可) です。一度初期化するとその要素の内容や要素の数を変更することはできません。それに対して各 Mutable クラスは初期化の後も要素の内容や数を変更することができます。以上で Objective-C でのコレクションクラスは合計6つということになります。なお、このように不変と可変のクラスが用意されている理由としては、可変 (Mutable) クラスよりも不変クラスのほうが内部構造が簡素で動作も軽るくなるので状況によって使い分けて欲しいとのことらしいです。
今回はこのコレクションクラスの中から NSMutableDictionary を使って複数の人の自己紹介文を登録できるようにしたいと思います。なお高速列挙 (Fast Enumeration) についてはこの learnObjC Lite では取り扱わないことにいたしました。理由としては Array の場合には for 文の代わりとして高速列挙を使うことによってその効果を期待できますが、Dictionary の場合にはもともとキーで各要素を検索できますので高速列挙を使う場面があまりないということに気付いたからです。
なお高速列挙についてお知りになりたい方は
learnObjC 第5回 高速列挙 をご覧になってください。
サンプルプログラム
まずホームフォルダの lite フォルダの中に lite4 フォルダを作ってください。前回の lite3 をコピーしてそのファイルに変更を加えて書き直すこともできますが今回は構造が大きく変わりますので最初から作るようにいたします。その前に少し説明したいことがあります。
MVC
オブジェクト指向プログラミングには Model View Controller (MVC) というプログラミングデザインパターンがあります。プログラムの中をモデルパート、ビューパート、コントローラーパートに分けて設計しようというものです。MVC のそれぞれの役割については C++、Java、Objective-C、その他の言語によって少しずつ違います。ここでは Objective-C での MVC の役割に付いて簡単に説明します。なお各パートが1つのオブジェクトである必要はありません。とく View パートなどはボタン1つでも1つのオブジェクトになりますので多数のオブジェクトで View パートが構成されることになります。
Model
そのプログラムが扱うデータそのものの部分
View
GUI パーツであるウィンドウやボタンなどのユーザーの目から実際に見える部分で、ユーザーからの入力や指令 (命令) を受け取り、またプログラムが行った仕事の結果を表示する部分
Controller
Model と View を同期するもの。ユーザーによって View に入力や操作があればその指示に従って Model のデータを変更する。また Model のデータがプログラム内から変更された場合にはすぐにその変更を View の見た目に反映させるもの
MVC は初のオブジェクト指向言語である smalltalk で提唱されたプログラム設計思想です。smalltalk は今で言うところの GUI (グラフィックユーザーインターフェース) と一体になった開発環境であったみたいです。また View という言葉にははっきりと見た目という意味が含まれていると思います。ですので MVC は GUI アプリケーションでのみ有効なデザインパターンであると考えて良いでしょう。
しかし前回までは ITModel というモデルオブジェクトを持っていました。これはどちらかというと勉強のために作ったモデルパートだったと思いますが今回は ITController というコントローラーパートを模したものを作ります。正確にはコントローラーパートと純粋に合致してるとは言えませんがこれを作ることによってプログラムの可読性 (コードの見やすさ、読みやすさ) は向上していると思います。
このようにプログラムをある規則 (デザインパターン、設計様式) に従って作っていくことは非常に重要な事だと思います。なぜなら規則性もなくコードを書いていくと、あっと言う間に作者自身でさえもコードのどこが何をしているのか、何かをするコードがどこにあるのかが分からなくなるからです。ある意味でコメントを書くことよりも重要と言えるでしょう。
では早速サンプルプログラムを作っていきます。下記の3つのファイルをコーディングして lite4 フォルダに保存します。
ITController.h
ITController.m
main.m
ファイルが出来上がりましたらターミナルを起動して
cd lite/lite4
で lite4 フォルダに移動します。コンパイルコマンドは
gcc main.m ITController.m -o intro -framework Foundation -fobjc-gc -wall
です。
実行は
./intro
です。
なおこのサンプルコードは
lite4.zip
からダウンロードすることもできます。
実行画面
実行画面を見ても、そろそろ CUI (キャラクター[文字]ユーザーインターフェース) としても限界にきていると思います :-)。早くこの learnObjC Lite を終らせて GUI アプリケーションへ以降すべき時ですね。
今回は手早く終らせるために大方のコードは理解されているものとしてかいつまんで説明していきます。
コード説明
ITController.h
最初の4行で必要なプリプロセスをすべてこのヘッダファイルで行っています。読み込み順を考えるとここで行うのが順当だと思います。ただし注意しなければいけないのは Xcode というべきか、付属の gcc というべきかが非常に優秀で C についてのヘッダファイルは読み込まなくても問題なくコンパイルできてしまいます。従ってもし間違っていたとしても分からないということもあります。
6行目〜9行目
今回のインスタンス変数は NSMutableDictionary (変更可能辞書) クラス型が1つだけです。このようにコントローラーの中にモデルパートを含めることは簡単なプログラムでは良く行われます。
10行目
@property でインスタンス変数 dictionary のアクサセメソッドの宣言部分を自動合成しています。今回も属性には copy を付け加えています。copy 属性を付けない場合にはコンパイル時に「コピー属性を付けないと・・・」という警告が大量に出ます。しかしコンパイルは完了して実行ファイルは出来上がります。私の試した範囲では動作も問題ないように思えますが、一応警告に従い copy 属性を付けました。
11行目〜15行目
アクサセメソッド以外のインスタンスメソッドの宣言を行っています。合計5つになりますが何をするメソッドなのかが分かりやすいメソッド名にしました。
isRegistered
YES か NO (BOOL型) で答えられる「○○されているか?」というメソッドに付けられる Objective-C での一般的な命名方式「is○○」に従いました。今回のは辞書 (dictionary) に登録が1つ以上あるのかないのかを調べるメソッドで showIntro と removeIntro の中から呼び出されるメソッドです
input
名前や自己紹介文をターミナルの入力から受け取り、その C 文字列を文字列オブジェクト (NSString) に変換する部分を受け持ちます。プログラムの中で何回も登場してくるのでメソッド化しました。
showIntro
文字通り intro (自己紹介文) を表示するメソッドです。
addIntro
自己紹介文を追加するメソッドですが、正確にはキー (名前) と値 (自己紹介文) のペア (エントリー) を追加するメソッドです。
removeIntro
辞書から指定された名前 (キー) のエントリーを削除するメソッドです。
ITController.m
4行目
インスタンス変数 dictionary のアクサセメソッドの実装も自動合成することにしています。
init メソッド
スーパークラスの init メソッドを上書きしています。9行目の [ NSMutableDictionary dictionary ] は、クラスメソッド dictionary でレシーバーの NSMutableDictionary の空の (エントリーの1つもない) 一時的な辞書オブジェクトを返してインスタンス変数 dictionary に代入しています。
isRegistered メソッド
15行目 [ dictionary count ] は count メソッドでレシーバーの NSDictionary のエントリーの数を返します。エントリーがなければ 0 が返りますので else 節へ進みます。
input メソッド
今までに繰り返し説明してきた式で成り立っていますので説明は省略させていただきます
showIntro メソッド
35行目の isRegistered メソッドで dictionary のエントリーの数を調べています。結果として YES が返ってきた場合にのみこのメソッドは次に進みます。37行目で input メソッドを呼び出して (メッセージを送って) name 変数にその値 (文字列オブジェクト) を代入しています。38行目では辞書オブジェクトのインスタンスメソッドである objectForKey:キー を使って intro 変数にキーとペアになっている値を代入しています。このプログラムの場合は値も自己紹介文という文字列オブジェクトです。39行目で intro が nil でなかったらその文字列オブジェクトが C 文字列に変換されてターミナルに表示されます。dictionary に name 変数と同じキーがなかった場合には intro へは nil が代入され結果として else 句へ進みます。
addIntro メソッド
55行目 インスタンスメソッド objectForKey:キー はレシーバーの辞書に引数キーに対応する値を返します。キーに対応するエントリーがなければ nil が返されます。エントリーがなければ (nil が返されれば) 58行目の setObject:値オブジェクト forKey:キーオブジェクト メソッドでレシーバーの辞書に新しいエントリーを追加します。55行目で nil が返されなければ59行目の else 句にすすみます。
removeIntro
69行目の isRegistered メソッドで dictionary にエントリーが1つでもあるかどうか調べています。エントリーがある場合のみこのメソッドは次に進みます。72行目 先ほども説明しましたが objectForKey:キー でキーに該当するエントリーがあるかどうか調べています。もしキーに該当するエントリーがあれば removeObjectForKey:キー メソッドでキーに対応するエントリーをレシーバーの辞書から削除しています。72行目でキーに該当するエントリーがなければ75行目の else 句に進みます。
main.m
9行目
ITController クラスからインスタンスを作成して controller 変数に代入しています。
12行目〜32行目
イベントループを開始しています。本格的にはイベントループは別の関数として定義して使いますがここでは簡易的に main 関数の中でイベントループを開始しています。この部分についての説明はすでに終えていると思いますが、各 case 句の中が [ controller メソッド名 ]; とスッキリとしたかたちになりました。
ファイル入出力 plist
今回は Cocoa 独自で採用されているファイル入出力システムの plist でファイルを保存して読み込みます。plist で読み書きされるファイルもテキストファイルですが XML 方式で読み書きされます。取りあえずやってみましょう。
ファイル出力
ITController.h
ITController.h の最後 (@end) の前に - (void)writeFile; メソッド宣言を書き加えます。例では最後になっていますがインスタンス変数の宣言の終った } から @end までの間であればどこに記述しても構いません。
ITController.m
ITController.m にメソッド writeFile の実装を加えます。例では @end の前に (1番最後に) に書き加えていますが、記述する場所は @implementation から @end の間であればどこでも構いません。実装部分では辞書オブジェクトに writeToFile: ファイル名と保存場所の NSString 文字列 atomically: YES か NO の BOOL 値 というメソッドを送ります。これによって文字列オブジェクトで指定した場所とファイル名でファイルが書き出されます。ラベル atmically では YES とするとファイルを一度、一時ファイルに書き出されてから指定ファイルに書き出されます。
main.m
main.m の case 句 7: に [ controller writeFile ] ; メッセージ式を書き加えます。
コードの記述が出来ましたらターミナルを起動して
cd lite/lite4
で lite4 フォルダに移動します。コンパイルコマンドは
gcc main.m ITController.m -o intro -framework Foundation -fobjc-gc -wall
です。
実行は
./intro
です。
色々と自己紹介文を入力して intro プログラムを終了させてください。起動ディスクの第一階層の「ライブラリ」フォルダの中の「Preferences」フォルダに「ZZIntro.plist」というファイルが出来上がっていると思います。このファイルをダブルクリックすると Property List Editor というアプリケーションを使ってファイルが開きます。Property List Editor は慣れないと見にくいと思いますので一度終了させて ZZIntro.plist をマック付属のテキストエディタで開いて見ましょう。
ZZIntro.plist
XML ファイルなので読みにくいとは思いますが、人間の読めるテキストデータであることは確かです。しかし今のままでは次に intro プログラムを実行しても ZZIntro.plist は読み込まれません。続けてファイル入力のコードを書き加えましょう。
ファイル入力
ファイル入力のコードは ITController.m の init メソッドの中に直接記述します。
ITController.m init メソッド
10行目
NSDictionary のクラスメソッド dictionaryWithContentsOfFile: は引数で指定された場所のファイル名のファイルを読み込んで一時的な辞書オブジェクトを作成します。指定したファイルが存在しなかった場合などは nil が返されます。なお一時オブジェクトと言っていますが、今回のようにガベージコレクションを採用している場合はリファレンスカウンタ方式に関係することは一切無視されます。
13行目
10行目で dictionary インスタンス変数に nil が代入された場合には NSDictionary のクラスメソッド dictionary が実行されてインスタンス変数はエントリー数ゼロの一時辞書オブジェクトで初期化されます。
ではコンパイルしなおします。ターミナルを起動して
cd lite/lite4
で lite4 フォルダに移動します。コンパイルコマンドは
gcc main.m ITController.m -o intro -framework Foundation -fobjc-gc -wall
です。
実行は
./intro
です。
前の ZZIntro.plist が残っていればそのデータが読み込まれていることだろうと思います。intro プログラムを一度終了してから ZZIntro.plist を削除するか別の場所に移動してから再度 intro プログラムを起動すると何もデータの入っていない状態に戻っていると思います。
お疲れさまでした。これで「learnObjC Lite」の第4回を終ります。
learnObjC Lite は今回で終了したいと思います。そして learn C から始まった C および Objective-C の言語的側面を説明したこのシリーズは今回をもって終了したいと思います。読んで頂いてありがとうございました。
次回からは Cocoa フレームワークを使用した GUI アプリケーションを作っていきたいと思います。まず手始めにこの learnObjC Lite で作った intro プログラムを GUI 化しようかなと思っています。しかしその前にこのサイトに「日記」システムを Ruby で作りたいとも思っています。
ありがとうございました。
|
|
|