今回は、Modelオブジェクトのドキュメントファイルへの書き出しと読み込み処理を実装してみます。また、「しんぶんし 3」で、どのように画像ファイルを取り扱うのかも考えてみます。
まずは、Modelオブジェクトのドキュメント処理のために、新しくDocumentクラスを定義します。Documentクラスのおもな仕事は、以下の3つとなります。
・新規でModelオブジェクト用の配列(NSMutableArray)を作成しておく。
・ファイルをアンアーカイブ(デコード)してModelオブジェクトへ代入する。
・全てのModelオブジェクトをアーカイブ(エンコード)しファイルへ保存する。
つまり、ドキュメントの作成、ロード、セーブの3つの処理を担当するわけです。以下がDocumentクラスの定義(Document.h)となります。
#import <UIKit/UIKit.h>
@interface Document : NSObject
{
NSString *do_path; // ドキュメントファイルパス
NSMutableArray *do_array; // モデルの配列
}
@property(nonatomic,retain)NSString *do_path;
@property(nonatomic,retain)NSMutableArray *do_array;
- (BOOL)create; // 作成
- (BOOL)load; // ロード
- (BOOL)save; // セーブ
- (NSString *)path; // ファイルパスを得る
NSString *getDocumentPath( NSString *name ); // ユーティリティ
@end
インスタンス変数は、ドキュメントのファイルパス(保存場所)を保存しておくためのNSString *do_pathと、複数のModelオブジェクトを保存しておくための配列であるNSMutableArray *do_arrayの2つをとりあえず用意します。両方ともretainプロパティとして定義しておきます。これによりアクセッサメソッドを作成しなくともドット(.)演算子経由でアクセスすることができます。メソッドは create、load、saveの3つと、ドキュメントのファイルパスを得るためのpathの4つです。最後のgetDocumentPath()は、iPhoneアプリ固有のファイル保存場所(Documentディレクトリ)を返すCルーチンです。
Mac OS X用の一般的なCocoaアプリケーション(Documentタイプ)では、ドキュメントウィンドウをオープンするたびに、それに対応したDocumentオブジェクトがひとつ作成されます。そのため、複数のDocumentオブジェクトの管理を受け持つDocument Controllerクラスなどが別途用意されていました。しかしiPhone版の「しんぶんし3」では、ドキュメントはひとつしか扱いませんので、Document Controllerクラスを作ることはせず、その役割はSymmetryAppDelegateクラス(アプリケーション・デリゲート)に担ってもらうことにします。
まずは、SymmetryAppDelegateのクラス定義(SymmetryAppDelegate.h)にインスタンス変数Document *ap_documentを追加ます。
@class Document; // 先んじてクラス宣言しておかないとエラーになる
@interface SymmetryAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
UINavigationController *navigationController;
Document *ap_document; // 作成されるドキュメント
}
アプリケーション起動時に呼ばれるapplicationDidFinishLaunching:メソッドに、ドキュメントオブジェクトの作成とファイルの読み込み処理(アンアーカイブ)を追加します。対象となるソースファイルは、SymmetryAppDelegate.mです。ドキュメントの保存については、 アプリケーション終了時に呼ばれるapplicationWillTerminate: メソッドに記述します。どちらもUIApplicationのデリゲートです。
- (void)applicationDidFinishLaunching:(UIApplication *)application // 起動時
{
if( ap_document=[[Document alloc] init] ) // ドキュメント作成
{
if( [ap_document load]==NO ) // ドキュメント読み込み
{
// ファイルの作成や読み込みに失敗した場合(エラー処理)
}
}
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application // 終了時
{
[ap_document save]; // ドキュメントファイル保存
}
続いてDocumentクラスの実装(Document.m)です。ドキュメントの作成、ロード、セーブの3つのメソッドと、ひとつのCルーチンを記述します。ドキュメントファイルの名称は「Symmetry.archive」で固定します。
#import "Document.h"
@implementation Document
@synthesize do_path,do_array;
- (BOOL)create // ドキュメントの新規作成
{
BOOL ret=NO;
if( do_array=[[NSMutableArray alloc] init] ) // モデル配列を作成
ret=YES;
return ret;
}
- (BOOL)load // ドキュメントファイルの読み込み
{
BOOL ret=YES;
NSString *path;
path=[self pathDocument]; // Symmetry.archiveファイルのパスを得る
if( [[NSFileManager defaultManager] fileExistsAtPath:path]==YES )
{ // ファイルが存在しているか?
do_array=[[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];
// ファイルからdo_arrayt配列から読み込む
if( ! do_array ) // ファイル読み込み失敗
ret=NO;
}
else
ret=[self create]; // ファイルが存在しない場合はドキュメント新規作成
return ret;
}
- (BOOL)save // ドキュメントファイルへの書き出し
{
BOOL ret=NO;
if( do_array )
ret=[NSKeyedArchiver archiveRootObject:do_array toFile:[self path]];
// do_array配列をアカーブ化しSymmetry.archiveファイルへ保存
return ret;
}
- (NSString *)path // 保存用のドキュメントファイルのパスを返す
{
if( ! do_path ) // 既に設定済みならそのまま返す
self.do_path=getDocumentPath( @"Symmetry.archive" );
// ドキュメントファイル(Symmetry.archive)のパスを得る
return do_path;
}
最後はドキュメントディレクトリ経由のパス名を得るためのgetDocumentPath()ルーチンです。このルーチンは画像ファイルのロードやセーブでも利用する予定ですので、pathメソッドから分離して用意しておきます。こうした処理、インスタンス変数を扱わなければCルーチンとして用意しておけば、どのオブジェクトからも呼び出し可能で便利です。
NSString *getDocumentPath( NSString *name )
{
NSString *path=nil;
NSArray *paths;
NSString *dir;
paths=NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,NSUserDomainMask,YES );
// ドキュメントディレクトリーを含むリスト配列
if( [paths count] > 0 )
{
dir=[paths objectAtIndex:0]; // ドキュメントディレクトリそのもの
path=[dir stringByAppendingPathComponent:name];
// ァイルパス名の作成(名前を追加)
}
return path;
}
次回は、画像ファイルの話です。画像ファイルは、Modelオブジェクトのドキュメントからは分離して別に保存します。登録一覧(UITableView)に画像を表示するためには、画像ファイルから小サイズ画像(UIImage)を作る必要もあります。