iOS SDKによるiPhone/iPadアプリケーション開発入門

第4回データモデルの設計と実装

gihyo.jp/dev/serial/01/ios-sdk/0003"">前回はUINavigationViewControllerとUITableViewControllerを使って起動直後の一覧画面を作成しました。今回はデータモデルオブジェクトを設計・実装していきます。

データモデルの設計と実装

それではデータモデルの設計から入ります。今回は一つの目標をTargetオブジェクトで表し、Targetオブジェクトを管理するオブジェクトをTargetManagerとします。

クラス図は以下のとおりです。クラス図はastahの無料版であるastah communityで作成しました。

モデルのクラス図
モデルのクラス図

まずはTargetオブジェクトを実装します。これは目標名と目標時間、達成度を保持するだけの簡単なオブジェクトです。

Targetオブジェクト

Target.h
@interface Target : NSObject {
    NSString *name;
    NSInteger targetHour;
    NSInteger achievedHour;
}

@property (nonatomic,retain) NSString* name;
@property (nonatomic) NSInteger targetHour;
@property (nonatomic) NSInteger achievedHour;

@end
Target.m
@implementation Target

@synthesize name;
@synthesize targetHour;
@synthesize achievedHour;

-(void)dealloc {
    self.name = nil;
    [super dealloc];
}

@end

Target.mの実装では、メモリの開放のためにself.name = nilとしています。これはnameはpropertyによってretainと宣言されているため、新しい値が代入される前にreleaseが呼ばれることを利用しています。またObjective-Cではnilオブジェクトに対して行ったメソッド呼び出しは無視されるため、開放済のオブジェクトのメソッドを呼び出してしまったことによる予想外の不正終了を防止するためにも、オブジェクトを開放した後はその変数にnilを代入しておくのはよい習慣です。

TargetManagerオブジェクト

続いてTargetManagerオブジェクトを作成します。Targetの集合はNSMutableArrayオブジェクトで保持することにします。NSMutableArrayオブジェクト自体はTargetManagerの外からは見えないようにし、必要最小限のメソッドを公開するようにしておきます。これにより、このオブジェクトの実装がCore Dataフレームワークや、API経由でデータを取得するようになった際などにも柔軟に対応できるようになります。

まずはヘッダ部分です。ここではデータ保持用の_targets変数と、3つのメソッドを定義しています。_targets変数はクラス外部からアクセスできないようにするために、プロパティの宣言は行っていません。

TargetManager.h
@interface TargetManager : NSObject {
    NSMutableArray *_targets;
}

-(id)add:(Target*)target;
-(Target*)targetAtIndex:(NSInteger)index;
-(NSInteger)count;

@end

続いて実装部分です。_targetsを初期化する際にarrayWithCapacityを使用していますが、このメソッドは内部でautoreleaseが呼ばれているため、明示的にretainを呼んでオブジェクトを保持するようにしています。

TargetManager.m
-(id)init {
    self = [super init];
    if (self != nil) {
        _targets = [[NSMutableArray arrayWithCapacity:0] retain];
    }
    return self;
}

-(void)dealloc {
    [_targets release];
    [super dealloc];
}

-(id)add:(Target*)target {
    [_targets addObject:target];
    return self;
}

-(Target*)targetAtIndex:(NSInteger)index {
    return [_targets objectAtIndex:index];
}

-(NSInteger)count {
    return [_targets count];
}

データの永続化

これでモデルを操作できるようになりましたが、このままではアプリが終了するとデータが失われてしまうため、データの永続化方法を検討する必要があります。SDKで提供されている永続化機構にはCore Dataフレームワークを使う方法と、NSCodingプロトコルを実装する方法があります。

Core Dataフレームワークは高機能なフレームワークで、SQLを用いた比較的大きなデータを効率的に扱えるのが特徴ですが、それゆえに若干複雑でハードルが高い面があります。今回はシンプルに扱えるNSCodingプロトコルでの実装方法を紹介します。

NSCondingプロトコルの実装

Targetオブジェクトを保存するために、TargetクラスにNSCodingプロトコルのメソッドを実装します。initWithCoderメソッドでは、引数のdecoderオブジェクトから値を取り出し、復元したいオブジェクトにセットしていきます。反対にencodeWithCoderメソッドでは、引数のencoderオブジェクトに対して永続化したい情報を設定していきます。

Target.m
- (id)initWithCoder:(NSCoder *)decoder {
    self = [super init];
    if (self != nil) {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.targetHour = [decoder decodeIntegerForKey:@"targetHour"];
        self.achievedHour = [decoder decodeIntegerForKey:@"achievedHour"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:name forKey:@"name"];
    [encoder encodeInteger:targetHour forKey:@"targetHour"];
    [encoder encodeInteger:achievedHour forKey:@"achievedHour"];
}

ファイルへの入出力

続いてファイルへの入出力を行う部分です。NSCodingプロトコルを実装したオブジェクトであれば、NSKeyedArchiver, NSKeyedUnarchiverを使って簡単に保存/読込み処理を実装できます。読み込み時は、NSKeyedUnarchiverで復元したオブジェクトはautoreleaseされてしまうため、_targetsには直接代入せずにNSMutableArrayクラスのsetArrayメソッドで読み込んだデータを設定しています。NSMutableArrayオブジェクトは設定したオブジェクトはすべてretainされ、オブジェクト開放時にすべてreleaseされます。

TargetManager.m
-(void)load {
    NSString *path = [self dataFilePath];

    NSFileManager *fmgr = [NSFileManager defaultManager];
    if (![fmgr fileExistsAtPath:path]) {
        return;
    }

    [_targets removeAllObjects];
    [_targets setArray: [NSKeyedUnarchiver unarchiveObjectWithFile:path]];
}

-(void)save {
    NSString *path = [[self dataDirectory] stringByAppendingPathComponent:FILE_NAME];

    [NSKeyedArchiver archiveRootObject:_targets toFile:path];
}

まとめ

今回はデータモデルの設計と、NSCodingプロトコルを用いたオブジェクトの永続化方法について学びました。作成したコードはこちらになります。

次回は、今回作成したモデルを用いてUI部分を実装していきます。

おすすめ記事

記事・ニュース一覧