2012年2月29日水曜日

Core Dataの追加


ユーザーが選択した情報、ディスク保存したデータのメタ情報などを保存するのにCore Dataが利用できます。

Xcode 4.2ではSingle View Appplicationを選択してプロジェクト作成するとCore Dataを使用する設定が追加されないので、既存プロジェクトにCore Dataを追加します。
参照: Core Data Model Editor Help
  1. Data Model追加
    Xcodeのメニューから  File > New > New File... を選択。
    テンプレート選択ウインドウで iOS > Core Data > Data Model を選択。
    xcdatamodelファイルを保存。

  2. Entity追加(DBのTableに相当)
    Xcode左ペインでプロジェクト名をクリック。
    Xcode左ペインで追加されたxcdatamodelファイルを選択。
    Xcode下ペインの+Add Entityをクリック。
    追加された"Entity"の名前を変更。

  3. attribute追加(DBのITEMに相当)
    Attibutesグループの下側ツールバーの+をクリック。
    追加された"Attribute"の名前を変更。
    データ型を選択。
    必要に応じてPropertyを設定。

  4. NSManagedObjectのサブクラス追加
    Xcodeのメニューから Editor > Create Managed Object Subclass... を選択。
    ファイルの追加または置換を行うEntityを選択。
    既存ファイルがある場合は置換になるので注意。

  5. framework追加
    ビルド前にCoreData.frameforkをリンクに追加します。

以上で下準備完了ですが、当然これだけではCore Dataの読み書きはできません。

Appleのチュートリアルではアプリケーションデリゲートクラスに次の property、methodを追加します。
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;

アプリケーション終了時、バックラウンド移行時にデータを保存するのが望ましいため、Core Dataアクセスは アプリケーションデリゲート の管理下で行うのが自然です。しかし、上記のプロパティーとメソッドは必ずしもApplicationDelegateに実装する必要はありません。また、マルチスレッドでCore Dataにアクセスする場合は各々でNSManagedObjectContextインスタンスを持つ必要があるため、アプリケーションデリゲート以外でも同様の手順が必要になる場合があります。

複数のNSManagedObjectContextインスタンスが保持する状態は異なる場合があるので、マルチスレッドでCore Dataを扱うような場合は注意が必要です。同じデータの異なる状態を異なるContextで持つような状況が避けらればよいのですが、さもなければ対策が必要になります。Core Dataでは楽観的ロック(Optimistic Lock)によりsave:時に元データに変更があった場合に対処する方法、notificationにより他のcontextで変更されたときに通知を受け取り、データを最新状態にする方法が用意されています。

実装例

MYAppDelegate.h

#import <CoreData/CoreData.h>

@interface IDAppDelegate : UIResponder <UIApplicationDelegate>
    @property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;
    @property (nonatomic, strong, readonly) NSManagedObjectModel *managedObjectModel;
    @property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end

MYAppDelegate.m

@implementation IDAppDelegate
@synthesize managedObjectContext = _managedObjectContext;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;

//保存先ディレクトリ
- (NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

//_persistentStoreCoordinatorがnilなら作って返す。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {  
    if (_persistentStoreCoordinator == nil) {
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                          initWithManagedObjectModel:[self managedObjectModel]];


       NSURL *url =  [NSURL fileURLWithPath:[[self applicationDocumentsDirectory]
                                          stringByAppendingPathComponent: @"CoreData.sqlite"]];

        NSError *error = nil;
        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                          configuration:nil
                                                                                     URL:url
                                                                                 options:nil error:&error])
        {
            //エラー処理
            NSLog(@"persistentStoreCoordinator: Error %@, %@", error, [error userInfo]);
        }
    }
    return _persistentStoreCoordinator;
}

//_managedObjectModelがnilなら作って返す。
-(NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel == nil) {
        _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    }
    return _managedObjectModel;
}

//_managedObjectContextがnilなら作って返す。
- (NSManagedObjectContext *)managedObjectContext {
    if (_managedObjectContext == nil) {
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (coordinator != nil) {
            _managedObjectContext = [[NSManagedObjectContext alloc] init];
            [_managedObjectContext setPersistentStoreCoordinator:coordinator];
        }
    }
    return _managedObjectContext;
}

//_managedObjectContextのデータを保存する。
- (void)saveContext {
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
        //エラー処理
        NSLog(@"saveContext: %@", error.debugDescription);
    }
}

//アプリケーション終了時に保存
- (void)applicationWillTerminate:(UIApplication *)application
{
    [self saveContext];
}

//バックグランド移行時に保存
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self saveContext];
}

//メモリ警告時に保存、リセット
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    [self saveContext];
    [[self managedObjectContext] reset];
}

@end

開発中に項目の追加/削除があると古いデータを読み込めなくなります。
Core Data 自動マイグレーションを設定することをお勧めします。

Core Data 自動マイグレーション
core data modelの不要なバージョンを削除する方法

0 件のコメント: