今回は、「しんぶんし 3」で必要とされるだろうデータ構造(モデル・オブジェクト)について考えてみます。基本的には、複数の画像ファイルを管理するのに必要とされるデータ構造を用意します。
その前に、前回はウィンドウに表示させたいJPEG画像ファイルを用意し、プロジェクトの「Resources」グループに登録しておけば、これらの画像アイコンがInterface Builderのライブラリの「Media」タブに表示されると解説しました。しかし、Carbonプロジェクトの場合、表示用のPICT画像やアイコンがリソースファイルとして保存されているケースが多々あります。旧バージョンのInterface Builderでは、リソースファイルに登録した画像も、Interface Builder上でその内容を確認しながら編集作業(ボタンに貼付けるとか)が出来ました。
ところが、最新バージョンでは、それが表示されなくなってしまいました(ボタン上に画像が何も出てこない)。加えて、テキストフィールドへの文字入力がインスペクタ上でしかできないようになっていたりと、引き続きCarbonプロジェクトをメンテしなければいけない開発者にとっては不便きわまりない状態です。「ユーザインターフェースの処理はCocoaでやりなさい!」という話は分かるのですが、前に可能だったことを出来なくするのだけは止めていただきたいと思います。実作業に支障が出るのは大変に困ります。
さて、アプリケーションのメイン・データ構造を決定する場合には、そのアプリケーションが「何をするのか?」を考えれば良いことになります。「しんぶんし 3」の場合には、画像ファイルをオープンし、そのシンメトリ(対称)画像を作成して保存するという一連の作業を行います。一枚一枚画像ファイルをオープンしていては面倒ですので、画像ファイルを含むフォルダを選び、中の画像をすべて得るという処理も必要です。カット&ペーストやドラッグ&ドロップによる画像やフォルダの登録も必要でしょう。
例えば、複数の画像ファイルを管理するなら、とりあえずファイルの「パス名」リストとその「個数」を保存しておけばOKです。まあ、実際のシンメトリ処理には、その機能に付随する幾つかのアトリビュート(パラメータ)も必要となりますので、パス名だけという訳には行きませんが、今回のアプリケーションでは、それほど多くのパラメータは必要ないでしょう。Carbonで作成していた前バージョンは、画像ファイルの管理用としてパス名の代わりにFSSpec構造体を利用していました(これもパス名みたいなものですが...)。すべての管理用データは以下のようなImageFile構造体にまとめられています。
typedef struct {
FSSpec sys_fsc; // 画像ファルの保存場所を示すFSSpec構造体
Rect sys_srt; // 画像選択用の矩形枠情報
OSType sys_type; // 画像ファイルのタイプ
short sys_flag; // 状態の管理用フラグ
short sys_kind; // シンメトリ(対称)方向
short sys_para; // オプション用パラメータ
} ImageFile,*ImageFilePtr;
この構造体を予想される最大個数(前回は2000個に定義していました)確保し、画像ファイルの管理に利用します。ソースコードで書けば...
#define MAX_FILE 2000 // 最大が像ファイル管理個数
ImageFilePtr sys_image; // 構造体配列の先頭アドレス
sys_image=(ImegeFilePtr)NewPtrClear( MAX_FILE*sizeof(ImegeFile) );
if( sys_image==NULL )
// 確保できなかった場合のエラー処理
といった感じです。その後の処理で「n個目」の構造体を参照する場合には、sys_imageを起点として以下のようにアクセスします。
ImegeFilePtr nowImagePtr;
nowImagePtr=sys_image+n;
構造体データをすべてコピーしたければ、最近のコンパイラでは以下の書式でOKです。
ImageFile nowImage;
nowImage=*(sys_image+n);
この方法、構造体用メモリを一度に全部確保してしまいますので、必要となるメモリ容量の予想がしやすく(エラー処理も簡単)全体的な管理が楽です。また、逐次確保の手続きが不要な分だけデータアクセス処理が高速となります。欠点は、余分な(未使用)メモリ領域が確保されてしまっている点と、どうあがいても2000個以上登録できないと言うことです(笑)。しかし、今となっては2000を20000に変更したところで、消費されるメモリは2MByte程度ですので「どおってことない」という感じはします。
これをObjective-Cの「オブジェクト」として実装してみると、以下のような感じになります。上記の構造体のメンバーは、インスタンス変数として定義されます。今後の展開からして、CGRectはNSRectの方が良いかもしれません(内部構造的には同じ)。
@interface ImageFile : NSObject
{
NSString *_name;
NSString *_type;
CGRect _srt;
unsigned int _flag;
int _kind;
int _para;
}
@end
そして、モデルのコントロール・オブジェクトの方にNSMutableArrayのインスタンス変数を定義しておいて、その配列要素に、[[ImageFile alloc] init];で確保したImegeFileオブジェクトを登録して管理します。
@interface ImegeList : NSObject
{
NSMutableArray *_list;
}
@end
こちらの方法の利点は、登録個数に制限がない点です(Mac OS Xが管理できるメモリ容量最大までOK)。それに特定オブジェクトを削除したり順番を入れ替えたりする仕事は、NSMutableArrayのメソッドが引き受けてくれますので、リスト管理が大変楽です。先に説明した構造体一括管理の場合には、そうした処理が必要であれば自分で処理ルーチンを実装する必要があります(そんなに難しくはないですが...)。旧「しんぶんし」でも、そうした処理(削除のみで順番変更はなし)を独自で実装していました。
さて、 旧バージョンのデータ構造では、ドラッグ&ドロップで登録した画像ファイルはOKなのですが、カット&ペーストで登録したい画像(ファイルではなく画像データ)の情報を保存できません。だとすると、画像データ自体を保存しておくインスタンス変数も用意しておいた方が良いかもしれません。 旧バージョンでは、画像ファイル一覧(台帳)をファイルへ保存して再利用するという機能はありませんでした。しかし、今回はCocoaの練習の意味もあるので「台帳」のファイルへの保存も考えることにします。
であれば、パス名の管理だけでは、登録後にその画像が別の場所に移されてしまった時に台帳に登録したファイルが見つからない場合があります。Carbonアプリでは、構造体内にファイルの「AliasHandle」を保存しておくことで、Aliasマネージャがファイルを探し出してくれました。Cocoaではどうしたものでしょうか?また、台帳ファイルを保存するという機能を追加するとなれば、Cocoaのアプリケーション形式を「Cocoa Document-based Application」へと切り替えた方が良さそうです。色々と課題が山積しています。
次回は、「しんぶんし 3」プロジェクトを「Cocoa Document-based Application」に切り替えてみます。こちらのアプリケーションタイプで注意すべき点を調べながら、モデル・オブジェクトの実装を試みたいと思います。