Persistence(18)~Core Dataでの保存(5)

2011. 07. 26
完成したアプリケーションの動作は変わりませんが、同じSQLiteでのデータ保存でも前回の『Persistence(7)~SQLite3への保存(1)』に比べて記述するコード量が少なく、またSQLite3の文法を学ぶ必要が無い反面、Core Dataの概念を把握することに少し苦労するかもしれません。

今回は私の現在の開発環境がXcode 3.2.6/iOS 4.3であることから、(テンプレートで自動生成されるコードの都合上)iOS 4.x版のサンプルコード(Beginning iPhone 4 Development Exploring the iOS SDK)で話を進めてきました。

翻って、iPhone OS 3.x版のサンプルコード(Beginning iPhone 3 Development Exploring the iPhone SDK)との差異を確認してみます。

手元に『はじめてのiPhone3プログラミング』が無いので推測になりますが、アプリケーションデリゲートクラスの内容はOSのバージョンの違いによって、テンプレートで自動生成されるコードが異なるのかもしれません。


●Core_Data_PersistenceAppDelegate.h

@class PersistenceViewController;
@interface Core_Data_PersistenceAppDelegate : NSObject <UIApplicationDelegate> {

    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;
    NSPersistentStoreCoordinator *persistentStoreCoordinator;

    UIWindow *window;
    PersistenceViewController *rootController;
}

- (IBAction)saveAction:sender;


@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

@property (nonatomic, readonly)
NSString *applicationDocumentsDirectory;

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet PersistenceViewController *rootController;

@end

7442

1)UIKit.hとCoreData.hのインポート

iOSアプリケーションの基礎となるUIKit.hと、Core Dataの利用に不可欠なCoreData.hがインポートされていません。
Frameworksグループ内には入っていますが、(CoreData.hはオプション扱いとしても)UIKit.hが含まれていないのは少々不可解です。

2)@privateによる制限

Core Dataに関する3つのインスタンス変数に@privateによる制限がかけられていません。
このアプリケーションではサブクラスを生成しないので問題ありませんが、オリジナルのアプリケーションで手動でCore Dataを利用する際は注意が必要です。

3)Core Data関連のインスタンス変数名

Core Dataに関する3つのインスタンス変数名が異なります。
(変数名の末尾の『_』の有無)

4)管理オブジェクトコンテキストの保存メソッド

管理オブジェクトコンテキストを保存するメソッドがアクションメソッドのsaveAction:になっています。
iOS 4.x版のsaveContextメソッドとは処理が異なりますので注意してください。
(詳細は後述します)

5)ドキュメントディレクトリの取得メソッド

ドキュメントディレクトリを取得するメソッドapplicationDocumentsDirectoryが、単なるメソッドではなく、プロパティとして宣言されています。
readonlyが指定されているので、@synthesizeではなく通常の(ゲッタ)メソッドとして実装されています。


●Core_Data_PersistenceAppDelegate.m

#import "Core_Data_PersistenceAppDelegate.h"
#import "PersistenceViewController.h"

@implementation Core_Data_PersistenceAppDelegate

@synthesize window;
@synthesize rootController;

#pragma mark -
#pragma mark Application lifecycle

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    // Override point for customization after app launch

    [window addSubview:rootController.view];
    [window makeKeyAndVisible];
}

/**
applicationWillTerminate: saves changes in the application's managed object context before the application terminates.
*/

- (void)applicationWillTerminate:(UIApplication *)application {

    NSError *error;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Handle error
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            exit(-1); // Fail
        }
    }
}


#pragma mark -
#pragma mark Saving

/**
Performs the save action for the application, which is to send the save:
message to the application's managed object context.
*/

- (IBAction)saveAction:(id)sender {

    NSError *error;
    if (![[self managedObjectContext] save:&error]) {
        // Handle error
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1); // Fail
    }
}


#pragma mark -
#pragma mark Core Data stack

/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/

- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}

/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
*/

- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
    return managedObjectModel;
}

/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Core_Data_Persistence.sqlite"]];

    NSError *error;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
        NSLog(@"error adding persistent store");
        // Handle error
    }

    return persistentStoreCoordinator;
}

#pragma mark -
#pragma mark Application's documents directory

/**
Returns the path to the application's documents directory.
*/

- (NSString *)applicationDocumentsDirectory {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    return basePath;
}


#pragma mark -
#pragma mark Memory management

- (void)dealloc {

    [managedObjectContext release];
    [managedObjectModel release];
    [persistentStoreCoordinator release];

    [window release];
    [super dealloc];
}

@end

7443

1)アプリケーション起動直後の処理

古いテンプレートなのでapplication:didFinishLaunchingWithOptions:メソッドではなく、applicationDidFinishLaunching:メソッドが使用されています。

2)管理オブジェクトコンテキストの保存のタイミング

applicationDidEnterBackground:メソッドはiOS 4.0以降で利用可能なため、管理オブジェクトコンテキストの保存タイミングはapplicationWillTerminate:メソッドのみとなります。
(したがってiOS 4.0以降ではアプリケーションを終了させてもデータの保存ができません)

3)applicationWillTerminate:メソッドの実装

iOS 4.x版では管理オブジェクトコンテキストの保存にsaveContextメソッドを呼び出していますが、iPhone OS 3.xでは同様の内容を実装しています。

4)saveActionメソッド

saveActionメソッドはiOS 4.x版のsaveContextメソッドとは異なり、『ボタンをタップした際に呼び出してコンテキストを保存する』という使用法のようです。
saveContextメソッドでは、コンテキストの有無を確認した後、コンテキストの変更の有無を確認して保存しますが、saveActionメソッドは確認すること無く無条件で保存します。

5)managedObjectModelメソッド

managedObjectModelメソッドでは、管理オブジェクトモデルの呼び出し方法が異なります。
iOS 4.x版では(iOS 4.0以降で利用可能な)URLForResource:withExtension:メソッドを使用してモデルファイルを格納しているフォルダのURLを生成し、initWithContentsOfURL:メソッドで初期化したオブジェクトを取得しています。
iPhone OS 3.x版ではmergedModelFromBundles:メソッドを使用して、メインバンドル内の全てのモデルを結合して取得しています。
したがってiPhone OS 3.x版は、モデルファイルがメインバンドル直下に一つだけ存在することを前提にしており、バージョンの異なるモデルなど複数のモデルファイルの存在に対応していません。

6)persistenceStoreCoordinatorメソッド

persistenceStoreCoordinatorメソッドでは、永続ストアへのファイルURLパスの生成方法が異なります。
iOS 4.x版では(iOS 4.0以降で利用可能な)URLByAppendingPathComponent:メソッドを使用して、NSURLオブジェクトのままドキュメントディレクトリに永続ストアのファイル名を付加しています。
iPhone OS 3.x版ではNSStringオブジェクトとしてstringByAppendingPathComponent:メソッドで結合した後で、fileURLWithPath:メソッドでNSURLオブジェクトを生成しています。

7)applicationDocumentsDirectoryメソッド

applicationDocumentsDirectoryメソッドでも、ドキュメントディレクトリへのパスの生成方法が異なります。
iOS 4.x版では(iOS 4.x以降で利用可能な)URLsForDirectory:inDomains:メソッドを使用して、ドキュメントディレクトリをNSURLオブジェクトとして生成しています。
iPhone OS 3.x版ではNSSearchPathForDirectoriesInDomains関数でドキュメントディレクトリを検索し、NSStringオブジェクトとして生成しています。


●PersistenceViewController.m

1)管理オブジェクトコンテキストの保存のタイミング

管理オブジェクトコンテキストを保存するメソッドに、applicationWillResignActive:ではなくapplicationWillTerminate:を使用しています。
(メソッドの内容は同じです)

2)行番号を示す属性lineNumの型変換

viewDidLoadメソッドで、行番号を示すエンティティLineの属性lineNumをintegerValueメソッドで変換していません。

3)通知センターへの登録

viewDidLoadメソッドで、管理オブジェクトコンテキストの保存のタイミングに関連して、addObserver:selector:name:object:メソッドのセレクタがapplicationWillTerminate:に、通知名がUIApplicationWillTerminateNotificationになっています。


●管理オブジェクトモデルの場所

管理オブジェクトモデルファイルCore_Data_Persistence.xcdatamodelが、バージョン管理を行うためのCore_Data_Persistence.xcdatamodeldフォルダ下ではなく、Resourcesグループ直下に置かれています。


●ビルドと実行

このプロジェクトを(ベースSDKをiOS 4.3に変更して)『ビルドと実行』を行うと、『ld: warnig: directory '/Volumes/Skiiing2/CD/ViewBased/Unknown Path/System/Library/Frameworks' following -F not found』という警告が出ます。

ちくわプログラマにっき/[iphone]「/Volumes/Skiiing2/CD/ViewBased/Unknown Path」が無いよ問題』によりますと、ターゲットのビルド設定で、検索パスの『フレームワーク検索パス』に余計な文が入っているのが原因のようです。

7444

パスの中の『/Volumes/Skiiing2/CD/ViewBased/Unknown Path』を削除すると、この警告は出なくなります。

前述のように、このプロジェクトでは管理オブジェクトコンテキストの保存タイミングがUIApplicationWillTerminateNotificationapplicationWillTerminate:メソッドの呼び出し)になっているため、データが正常に保存されません。

データを保存するには、PersistenceViewController.mのapplicationWillTerminate:のメソッド名をapplicationWillResignActive:に書き換え(内容は変更不要)、viewDidLoadメソッド内のaddObserver:selector:name:object:メソッドで、セレクタをapplicationWillResignActive:に、通知名をUIApplicationWillResignActiveNotificationに修正してください。


●補足

ここまで気付かずにいましたが、PersistenceViewController.mのapplicationWillTerminate:(iOS 4.x版ではapplicationWillResignActive:)は引数がNSNotificationとなっており、(リンクを張っている)UIApplicationDelegateプロトコルのメソッドではなく、実際にはMac OS X用のNSApplicationDelegateプロトコルのメソッドになっています。
(Core_Data_PersistenceAppDelegate.mの方は自動生成されるせいか、UIApplicationDelegateプロトコルのメソッドになっています)

このままでも一応動作はしますが、素直にUIApplicationDelegateプロトコルのメソッドに(引数をUIApplicationに)した方がいいと思います。



参考文献

C言語/C言語の演算子について

ちくわプログラマにっき/[iphone]「/Volumes/Skiiing2/CD/ViewBased/Unknown Path」が無いよ問題

iOS Core Dataチュートリアル

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

Persistence(17)~Core Dataでの保存(4)

2011. 07. 25
●ビューコントローラのソースファイルPersistenceViewController.mの編集

ソースファイルでは、4つのテキストフィールドのプロパティの実装と、viewDidLoadメソッドでの永続ストアからテキストフィールドへ値を反映する機能の追加、そしてアプリケーションが非アクティブになる際にテキストフィールドの値を永続ストアへ保存するapplicationWillResignActive:メソッドの追加を行います。

#import "PersistenceViewController.h"

#import "Core_Data_PersistenceAppDelegate.h"


@implementation PersistenceViewController

@synthesize line1;
@synthesize line2;
@synthesize line3;
@synthesize line4;


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad {
    [super viewDidLoad];
    Core_Data_PersistenceAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line" inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];

    NSError *error;
    NSArray *objects = [context executeFetchRequest:request error:&error];
    if (objects == nil) {
        NSLog(@"There was an error!");
        // Do whatever error handling is appropriate
    }

    for (NSManagedObject *oneObject in objects) {
        NSNumber *lineNum = [oneObject valueForKey:@"lineNum"];
        NSString *lineText = [oneObject valueForKey:@"lineText"];

        NSString *fieldName = [NSString stringWithFormat:@"line%d", [lineNum integerValue]];
        UITextField *theField = [self valueForKey:fieldName];
        theField.text = lineText;
    }
    [request release];

    UIApplication *app = [UIApplication sharedApplication];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];

}

- (void)applicationWillResignActive:(NSNotification *)notification {
    Core_Data_PersistenceAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSError *error;
    for (int i = 1; i <= 4; i++) {
        NSString *fieldName = [NSString stringWithFormat:@"line%d", i];
        UITextField *theField = [self valueForKey:fieldName];

        NSFetchRequest *request = [[NSFetchRequest alloc] init];

        NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line" inManagedObjectContext:context];
        [request setEntity:entityDescription];
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"(lineNum = %d)", i];
        [request setPredicate:pred];

        NSManagedObject *theLine = nil;

        NSArray *objects = [context executeFetchRequest:request error:&error];

        if (objects == nil) {
            NSLog(@"There was an error!");
            // Do whatever error handling is appropriate
        }
        if ([objects count] > 0)
            theLine = [objects objectAtIndex:0];
        else
            theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line" inManagedObjectContext:context];

        [theLine setValue:[NSNumber numberWithInt:i] forKey:@"lineNum"];
        [theLine setValue:theField.text forKey:@"lineText"];

        [request release];
    }
    [context save:&error];
}


- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    self.line1 = nil;
    self.line2 = nil;
    self.line3 = nil;
    self.line4 = nil;

}

- (void)dealloc {
    [line1 release];
    [line2 release];
    [line3 release];
    [line4 release];

    [super dealloc];
}

@end

7439

1)Core_Data_PersistenceAppDelegate.hのインポート

永続ストアとのデータの読み込み/書き込みを行うため、アプリケーションデリゲートのヘッダファイルをインポートしています。

2)4つのプロパティの実装

ヘッダファイルで宣言した4つのテキストフィールドのプロパティを実装します。

3)不要なメソッドの削除

initWithNibName:bundle:shouldAutorotateToInterfaceOrientation:メソッドは今回使用しないので削除しています。

4)viewDidLoadメソッドの編集

viewDidLoadメソッドでは、永続ストアから保存されているデータを読み込んでテキストフィールドに反映させる処理と、通知センターを作成してアプリケーションが非アクティブになる際にapplicationWillTerminate:メソッドを呼び出す処理を行っています。

実際の保存データの読み込みは、アプリケーションのインスタンスのデリゲートから管理オブジェクトコンテキストを取得し、フェッチ要求で管理オブジェクトとして保存されている内容を取得、高速列挙で内容をテキストフィールドに設定するという流れになっています。

まずsharedApplicationメソッドでアプリケーションのインスタンスを生成し、delegateプロパティでデリゲートappDelegateを取得します。

デリゲートからCore_Data_PersistenceAppDelegateクラスのmanagedObjectContextプロパティで管理オブジェクトコンテキストcontextを取得します。

そのコンテキストからentityForName:inManagedObjectContext:メソッドで名前が『Line』のエンティティを取得するためのエンティティ記述entityDescriptionを作成し、setEntity:メソッドでフェッチ要求requestとして設定します。

このフェッチ要求をコンテキストに対してexecuteFetchRequest:error:メソッドで実行し、エンティティ名Lineの管理オブジェクトを配列objectsとして取得します。

取得した管理オブジェクトの配列は、高速列挙でテキストフィールドに反映させます。

管理オブジェクトから、属性『lineNum』をキーとしてvalueForKey:メソッドで行番号lineNumを、属性『lineText』をキーとしてvalueForKey:メソッドで文字列lineTextを取得します。

行番号lineNumは、integerValueメソッドで整数値に変換し、stringWithFormat:メソッドでテキストフィールドのプロパティ名『line~』となるように合成されます。

合成された文字列fieldNameを用いて、valueForKey:メソッドで当該テキストフィールドのプロパティを取得し、textプロパティで管理オブジェクトから取り出した文字列lineTextを設定します。
(『Persistence(10)~SQLite3への保存(4)』の『2)viewDidLoadの編集』の項を参照)

最後にアプリケーションが非アクティブになる際にapplicationWillTerminate:メソッドを呼び出す仕掛けをします。

sharedApplicationメソッドで別のアプリケーションのインスタンスを生成し、defaultCenterメソッドで通知センターを用意し、addObserver:selector:name:object:メソッドで非アクティブの通知UIApplicationWillResignActiveNotificationがポストされた時にapplicationWillTerminate:メソッドを呼び出すように設定します。

entityForName:inManagedObjectContext:

+ (NSEntityDescription *)entityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context

指定した管理オブジェクトコンテキストの永続ストアコーディネータに関連付けされた管理オブジェクトモデルから、指定した名前のエンティティを返します。

このメソッドは以下のコード例と機能的に同等です。

NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:entityName];
return entity;

entityName:エンティティの名前を指定します。

context:使用する管理オブジェクトコンテキストを指定します。

setEntity:

- (void)setEntity:(NSEntityDescription *)entity

レシーバのエンティティを設定します。

entity:レシーバのエンティティを指定します。

executeFetchRequest:error:

- (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error

与えたフェッチ要求によって指定された基準を満たす、オブジェクトの配列を返します。

戻り値は、レシーバの永続ストアコーディネータに関連付けされた永続ストアから、レシーバからフェッチされた要求によって指定された基準を満たすオブジェクトです。

エラーが発生した場合はnilを返します。

リクエストで指定された基準に一致するオブジェクトが無かった場合は、空の配列を返します。

返されたオブジェクトは、レシーバに登録されます。

考慮すべき重要な点は以下の通りです。

  • フェッチ要求に述語が無い場合、指定されたエンティティの全てのインスタンスを取得し、以下の基準に従います。

  • (リクエストによって指定されたエンティティのインスタンスで、もしあるならリクエストの述語と一致する)requestによって指定された基準を満たし、コンテキストに挿入されているが永続ストアにまだ保存されていないオブジェクトは、フェッチ要求がコンテキスト上で実行された場合に取得されます。

  • コンテキスト内のオブジェクトが変更されている場合、永続ストア内の現在の状態に対してではなく、述語は変更された状態と比較して評価します。
    したがって、コンテキスト内のオブジェクトがフェッチ要求の基準を満たすように変更されていた場合、リクエストはたとえ変更がストアに保存されていない場合でも、基準を満たしていないストア内の値を取得します。
    逆に、コンテキスト内のオブジェクトがフェッチ要求と一致しないように変更されていた場合、フェッチ要求はストア内のバージョンと一致していても取得しません。

  • オブジェクトがコンテキストから削除されている場合、フェッチ要求はたとえその削除がストアに保存されていなくても、取得しません。
更新の保留や挿入、削除だけでなく、(populated、faults fired、『read from』など)実体化されたオブジェクトは、開発者の介入無しにフェッチ操作によって変更されることはありません。

いくつかのオブジェクトをフェッチする場合は、それらを一緒に処理し、新しいインスタンスの取得または既存のオブジェクトのデータ更新はしないで(現在のインメモリの状態を持つ既存のオブジェクトを取得し)、それからそれらのオブジェクトのスーパーセットを含む新しいフェッチを実行してください。

request:フェッチの検索基準を指定するフェッチ要求を指定します。

error:フェッチを実行して問題があった場合、問題を説明するNSErrorのインスタンスを含めて返します。

5)applicationWillTerminate:メソッドの追加

applicationWillTerminate:メソッドはviewDidLoadと逆の手順で、アプリケーションが非アクティブになる際に、テキストフィールドに入力されている文字列を管理オブジェクトに設定し、フェッチ要求で管理オブジェクトコンテキストを通して永続ストアに保存する処理を行っています。

最初にviewDidLoadと同様に、アプリケーションのインスタンスとデリゲートを準備し、管理オブジェクトコンテキストのインスタンスcontextを取得します。

次にforループで4つのテキストフィールドを1行ずつ処理します。

stringWithFormat:メソッドでテキストフィールドのプロパティ名を作成し、valueForKey:メソッドでプロパティを取得します。

コンテキストからentityForName:inManagedObjectContext:メソッドで名前が『Line』のエンティティを取得するためのエンティティ記述entityDescriptionを作成し、setEntity:メソッドでフェッチ要求requestとして設定します。

そしてpredicateWithFormat:メソッドで行番号を指定する述語を生成し、setPredicate:メソッドで述語を設定して、Lineエンティティから当該行だけを抽出します。

このフェッチ要求をコンテキストに対してexecuteFetchRequest:error:メソッドで実行し、エンティティ名Lineの管理オブジェクトを配列objectsとして取得します。

管理オブジェクトの配列objectsがnil、つまりLineエンティティが無かった場合はNSLogでエラーを出力します。
(本来は適切なエラー処理を行う必要があります)

配列objectsの要素数が0より上の場合、つまり既に永続ストアに当該行のデータが保存されていた場合は、objectAtIndex:メソッドで配列のインデックス0の要素を管理オブジェクトtheLineに設定します。

要素数が0の場合、つまり永続ストアに当該行のデータが無かった場合は、insertNewObjectForEntityForName:inManagedObjectContext:メソッドでエンティティ名Lineの管理オブジェクトを新規に生成してtheLineに設定します。

後は管理オブジェクトtheLineにsetValue:forKey:メソッドを用いて、テキストフィールドの行番号lineNumと文字列lineTextを設定します。

全てのテキストフィールドに対して管理オブジェクトの設定が終わったら、save:メソッドで未保存の変更を永続ストアに保存します。

predicateWithFormat:

+ (NSPredicate *)predicateWithFormat:(NSString *)format...

指定したフォーマットで新しい文字列を生成し、解析した結果によって構成された新しい述語を生成して返します。

フォーマット文字列の書式と変数の代入での制限の詳細については、『Predicate Format String Syntax』を参照してください。

format:新しい述語のためのフォーマット文字列を指定します。

...:formatに代入する引数をコンマ区切りのリストで指定します。

setPredicate:

- (void)setPredicate:(NSPredicate *)predicate

レシーバの述語を設定します。

predicate:レシーバの述語を指定します。

insertNewObjectForEntityForName:inManagedObjectContext:

+ (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context

指定した名前でエンティティのクラスのインスタンスを生成、構成をして返します。

戻り値は、entityNameで名付けられたエンティティのクラスのインスタンスを、新規に完全に構成して自動解放します。

インスタンスはエンティティ記述のセットを持ち、contextに挿入されます。

このメソッドは、管理オブジェクトの生成の詳細について心配すること無く、指定したエンティティのインスタンスを簡単に生成することができます。

メソッドはMac OS X v10.4で特に役立ち、エンティティを表すために使用するクラスを知らなくても、新規に管理オブジェクトの生成に使用することができます。

これはクラスとクラス名が揮発性の場合、初期の開発ライフサイクルにおいて特に有効です。

メソッドは、以下のコード例と概念的に類似しています。

NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:entityName];
NSString *className = [entity managedObjectClassName];
Class entityClass = [[NSBundle mainBundle] classNamed:className];
id newObject = [[entityClass alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
return [newObject autorelease];

Mac OS X v10.5以降とiOSでは、エンティティのための適切なクラスのインスタンスを返す際に、initWithEntity:insertIntoManagedObjectContext:の代わりに使用することができます。

Mac OS X v10.5とiOSの同等なコードは以下のようになります。

NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:entityName];
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
return [newObject autorelease];

重要:メソッド名に『new』という言葉が存在しますが、参照カウントの環境において、返されたオブジェクトを解放する責任はありません。
(『new』がメソッド名の最初の言葉ではないからで、『Memory Management Rules』を参照してください)

entityName:エンティティの名前を指定します。

context:使用する管理オブジェクトコンテキストを指定します。


●アプリケーションデリゲートのソースファイルCore_Data_PersistenceAppDelegate.mの編集

ウィンドウにビューを追加します。
(太字が追加した部分)

#import "Core_Data_PersistenceAppDelegate.h"
#import "PersistenceViewController.h"

@implementation Core_Data_PersistenceAppDelegate

@synthesize window;
@synthesize rootController;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.
    [self.window addSubview:rootController.view];
    [self.window makeKeyAndVisible];

    return YES;
}

7436

1)ビューコントローラのインポート

ビューを追加するため、ビューコントローラPersistenceViewController.hをインポートします。

2)プロパティの実装

ビューコントローラのプロパティrootControllerを実装します。

3)ウィンドウへのビューの追加

addSubview:メソッドで、ウィンドウにビューコントローラのビューを追加します。


●ビルドと実行

編集が終わったら『ビルドと実行』を行い、動作を確認します。

7441



参考文献

iOS Core Dataチュートリアル

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

Persistence(16)~Core Dataでの保存(3)

2011. 07. 24
●ビューコントローラの追加

ビューコントローラのソース/ヘッダファイルとnibファイルを追加します。

Classesフォルダを右クリックして、コンテキストメニューから『追加』→『新規ファイル...』を選択します。

7410

『Cocoa Touch Class』の『UIViewController subclass』を選択し、オプションの『With XIB for user interface』にチェックを入れ、ファイル名を『PersistenceViewController.m』とします。

7411

Classesフォルダ下にソース/ヘッダファイルだけでなくnibファイルも置かれるので、ドラッグ&ドロップでPersistenceViewContoller.xibファイルをResourcesフォルダ下に移動します。

7412


●ビューコントローラのヘッダファイルPersistenceViewController.hの編集

4つのテキストフィールドのアウトレットとなるプロパティを宣言します。
(太字が追加した部分)

#import <UIKit/UIKit.h>

@interface PersistenceViewController : UIViewController {
    UITextField *line1;
    UITextField *line2;
    UITextField *line3;
    UITextField *line4;

}

@property (nonatomic, retain) IBOutlet UITextField *line1;
@property (nonatomic, retain) IBOutlet UITextField *line2;
@property (nonatomic, retain) IBOutlet UITextField *line3;
@property (nonatomic, retain) IBOutlet UITextField *line4;


@end

7413


●アプリケーションデリゲートのヘッダファイルCore_Data_PersistenceAppDelegate.hの編集

アプリケーションデリゲートクラスにビューコントローラPersistenceViewControllerのビューを追加します。
(太字が追加した部分)

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@class PersistenceViewController;

@interface Core_Data_PersistenceAppDelegate : NSObject <UIApplicationDelegate> {

    UIWindow *window;

@private
    NSManagedObjectContext *managedObjectContext_;
    NSManagedObjectModel *managedObjectModel_;
    NSPersistentStoreCoordinator *persistentStoreCoordinator_;

    PersistenceViewController *rootController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

@property (nonatomic, retain) IBOutlet PersistenceViewController *rootController;

- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;

@end

7435

1)ビューコントローラの前方宣言

ウィンドウにビューを組み込むため、ビューコントローラのクラスPersistenceViewControllerを@classによる前方宣言でクラス名だとコンパイラに指示します。

2)ビューのインスタンス変数の宣言の追加

ビューコントローラのインスタンス変数rootControllerを追加しています。
(別のコンパイラ指示子を挟んでいないのでこの変数も@privateに入っていますが、特にprivateである必要は無いので、ウィンドウの直後に宣言しても構わないと思われます)

3)ビューのプロパティの宣言の追加

ビューコントローラをアウトレットとしてプロパティを宣言します。


●ウィンドウのnibファイルMainWindow.xibの編集

ウィンドウにビューを追加するため、MainWindow.xibを編集します。

Resources下にあるウィンドウのnibファイルMainWindow.xibをダブルクリックし、Interface Builderで開きます。

LibraryウィンドウからDocumentウィンドウに『View Controller』をドラッグ&ドロップして追加し、InspectorウィンドウでIdentityタブを開き、『Class』を『PersistenceViewController』に変更します。

7437

Documentウィンドウで『Core_Data_Persistence App Delegate』を選択し、InspectorウィンドウでConnectionタブを開き、Outletsの『rootController』を先程の『Persistence View Controller』に接続します。

7438


●ビューコントローラのnibファイルPersistenceViewController.xibの編集

ビューに各4つのラベルとテキストフィールドを追加するため、PersistenceViewController.xibを編集します。

Resources下に移動したビューコントローラのnibファイルPersistenceViewController.xibをダブルクリックし、Interface Builderで開きます。

LibraryウィンドウからViewウィンドウへ、LabelとText Fieldを4つずつドラッグ&ドロップして追加します。

各パーツの位置とサイズは下表の通りです。

LabelX:Y:W:H:Text FieldX:Y:W:H:
Line 1:20235321 812021931
Line 2:20625321 815921931
Line 3:201015321 819821931
Line 4:201405321 8113721931

ラベルのタイトルは、InspectorウィンドウのAttributesタブを開き、LabelのTextで変更します。

7414

DocumentウィンドウでFile's Ownerを選択し、INspectorウィンドウのConnectionsタブを開いて、アウトレットをViewウィンドウのテキストフィールドに接続します。

7415


●データのモデリング

永続ストアに保存するCore Dataのモデルを作成します。

モデルは一つのエンティティLineを持ち、何行目かを表すlineNumとテキストフィールド内の文字列を保持するlineTextの2つの属性があります。

1)エンティティの追加

XcodeでResourcesのCore_Data_Persistence.xcdatamodeld下にあるCore_Data_Persistence.xcdatamodelを選択し、モデルエディタを表示します。

7430

エンティティペインの左下角にある『+』ボタンでエンティティを追加し、詳細ペインで名前を『Line』に変更します。
(クラスはNSManagedObjectのままです)

7431

2)属性の追加

プロパティペインの左下角にある『+』ボタンをクリックして『属性を追加』を選択し、詳細ペインで名前を『lineNum』に、データ型を『整数16』に変更します。

7432

同様にもう一つ属性を追加し、詳細ペインで名前を『lineText』に、データ型を『文字列』に変更します。

7433

作業が完了したら保存します。



参考文献

iOS Core Dataチュートリアル

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

Persistence(15)~Core Dataでの保存(2)

2011. 07. 23
続いてソースファイルCore_Data_PersistenceAppDelegate.mに追加される内容を見てみます。
(太字が追加された部分)

#import "Core_Data_PersistenceAppDelegate.h"

@implementation Core_Data_PersistenceAppDelegate

@synthesize window;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [self.window makeKeyAndVisible];

    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [self saveContext];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
}

- (void)applicationWillTerminate:(UIApplication *)application {
    [self saveContext];
}

- (void)saveContext {

    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {

            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

#pragma mark -
#pragma mark Core Data stack

- (NSManagedObjectContext *)managedObjectContext {

    if (managedObjectContext_ != nil) {
        return managedObjectContext_;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext_ = [[NSManagedObjectContext alloc] init];
        [managedObjectContext_ setPersistentStoreCoordinator:coordinator];
    }
    return managedObjectContext_;
}

- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel_ != nil) {
        return managedObjectModel_;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Core_Data_Persistence" withExtension:@"momd"];
    managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return managedObjectModel_;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator_ != nil) {
        return persistentStoreCoordinator_;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Core_Data_Persistence.sqlite"];

    NSError *error = nil;
    persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return persistentStoreCoordinator_;
}

#pragma mark -
#pragma mark Application's Documents directory

- (NSURL *)applicationDocumentsDirectory {
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}


#pragma mark -
#pragma mark Memory management

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}

- (void)dealloc {

    [managedObjectContext_ release];
    [managedObjectModel_ release];
    [persistentStoreCoordinator_ release];


    [window release];
    [super dealloc];
}

@end

7425

1)saveContextメソッドの呼び出し

アプリケーションがバックグラウンドになった際に呼び出されるapplicationDidEnterBackground:と、アプリケーションが終了する直前に呼び出されるapplicationWillTerminate:で、管理オブジェクトコンテキストを永続ストアに保存するsaveContextメソッドが呼び出されています。

2)saveContextメソッドの追加

saveContextは、管理オブジェクトコンテキストを取得して保存するメソッドです。

コンテキストが存在しない場合は何もしません。

まず、後述する(ヘッダファイルで宣言したプロパティのゲッタである)managedObjectContextメソッドで管理オブジェクトコンテキストを取得します。

コンテキストが存在する場合は、hasChangeメソッドでコンテキストの変更の有無と、save:メソッドで未保存の変更の保存を試みた成否を判定します。

コンテキストが存在し、コンテキストに変更が加えられていて且つ保存ができなかった場合は、NSLogでエラーを出力し、abort()で異常終了します。
(abort()については『Programming Place Plus/C言語編 第65章 終了関数』を参照してください)

hasChanges

- (BOOL)hasChanges

レシーバの変更がコミットされていないかを示すブール値を返します。

戻り値は、レシーバの変更がコミットされていない場合はYES、そうでない場合はNOを返します。

Mac OS X v10.6以降では、このプロパティはキー値監視に準拠しています。
(『Key-Value Observing Programming Guide』参照)

Mac OS X v10.6より前では、このプロパティはキー値監視に準拠しておらず、例えばCocoaバインディングを使用している場合、管理オブジェクトコンテキストのhasChangeプロパティへバインドすることはできません。

キー値監視(KVO)を使用してこのプロパティを監視する場合、コンテキストに触れないようにするか、または通知のためにobserveValueForKeyPath:ofObject:change:context:の実装でそのオブジェクトを含める必要があります。

(これはKVO通知のロケーションが複雑なためで、例えばコンテキストはアンドゥの操作やマージの競合を修復している途中の可能性があるからです)

hasChangesの値が変化した結果として、その変更された管理オブジェクトのコンテキストへメッセージを送信する場合は、その後でコールスタックをアンワインドする必要があります。
(通常、performSelector:withObject:afterDelay:または類似したメソッドを使用します)

save:

- (BOOL)save:(NSError **)error

永続ストアへ登録されたオブジェクトへの未保存の変更をコミットしようと試みます。

戻り値は、保存が成功した場合はYES、それ以外はNOを返します。

(例えば、いくつかの編集されたオブジェクトが検証に失敗した場合など)複数のエラーが発生した場合、複数のエラーが示されたNSErrorの記述が返され、userInfo辞書にキーNSDetailedErrorsが含まれます。

NSDetailedErrorsキーに関連付けられた値は、個々のNSErrorオブジェクトを含む配列です。

error:NSErrorオブジェクトへのポインタです。
NSErrorオブジェクトを生成する必要はありません。
NULLを渡した場合、最初の失敗の後に保存処理が中断します。

3)managedObjectContextメソッドの追加

managedObjectContextはアプリケーション用の管理オブジェクトコンテキストを返すメソッドで、コンテキストが存在していない場合は生成し、アプリケーションの永続ストアコーディネータに関連付けします。

最初にコンテキストのインスタンス変数managedObjectContext_の存在の有無を判別し、あればそのままコンテキストを返します。

コンテキストが無い場合は、後述する(ヘッダファイルで宣言したプロパティのゲッタである)persistentStoreCoordinatorメソッドで永続ストアコーディネータを取得します。

コーディネータが存在する場合は、managedObjectContext_を生成・初期化してsetPersistentStoreCoordinator:メソッドでコーディネータを設定し、無い場合はそのままmanagedObjectContext_を返します。

setPersistentStoreCoordinator:

- (void)setPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator

レシーバの永続ストアコーディネータを設定します。

コーディネータは管理オブジェクトモデルの提供し、永続性を処理します。

複数のコンテキストがコーディネータを共有できることに注意してください。

coordinatorがnilの場合、このメソッドは例外を発生させます。

永続ストアコーディネータからコンテキストを『切断』する場合、単純にコンテキストへの全ての参照を解放するだけでなく、正常に割り当てを解除する必要があります。

coordinator:レシーバの永続ストアコーディネータ

4)managedObjectModelメソッドの追加

managedObjectModelはアプリケーション用の管理オブジェクトモデルを返すメソッドで、モデルが存在しない場合はアプリケーションのモデルから生成します。

最初にモデルのインスタンス変数managedObjectModel_の存在の有無を判別し、あればそのままモデルを返します。

モデルが無い場合は、mainBundleメソッドでアプリケーションバンドルの位置を取得し、URLForResource:withExtension:メソッドでモデルファイルの場所を特定します。

URLForResource:withExtension:メソッドの第一引数のnameはプロジェクト名が流用され、第二引数のextensionはモデルファイルが入っているディレクトリを表す『momd』が指定されています。

Core Dataのファイル『Core_Data_Persistence.xcdatamodel』は、Xcodeの『グループとファイル』ではResourceグループの直下ではなく、『Core_Data_Persistence.xcdatamodeld』の下に置かれています。

これは、アプリケーションバンドル内では『Core_Data_Persistence.momd』フォルダ内にモデルファイル『Core_Data_Persistence.mom』があるという形になります。

iPhone OS 3.x用のサンプルコードのように、Resourceグループ直下にモデルファイル『Core_Data_Persistence.xcdatamodel』を置いた場合、アプリケーションバンドル内にディレクトリ『Core_Data_Persistence.momd』は生成されず、モデルファイル『Core_Data_Persistence.mom』がそのまま置かれる形になります。

『~.xcdatamodeld』はモデルファイルのバージョン管理を行うためのもので、モデルをバージョンアップすると『~.xcdatamodeld』グループ下に新たなモデルファイル『~.xcdatamodel』が作成されることになります。

つまりiPhone OS 3.x用のサンプルコードでは、モデルファイルのバージョン管理に対応していないということです。

手動でCore Dataを利用する際、モデルの更新が不要であれば、『~.xcdatamodeld』グループを作成せずResource直下に『~.xcdatamodel』を置くことになると思いますが、その際に指定する拡張子は『momd』ではなく『mom』になることに注意してください。

詳細は『Cocoaの日々/[iOS][Mac] CoreData - マイグレーション[4] モデルファイルの構成』を参照してください。

特定したモデルファイルの場所のURLを用い、initWithContentsOfURL:メソッドでモデルを初期化して返します。

initWithContentsOfURL:

- (id)initWithContentsOfURL:(NSURL *)url

指定したURLにあるモデルファイルを使用してレシーバを初期化します。

戻り値は、urlにあるファイルを使用して初期化された管理オブジェクトモデルです。

url:モデルファイルの場所を指定するURLオブジェクトです。

5)persistentStoreCoordinatorの追加

persistentStoreCoordinatorはアプリケーション用の永続ストアコーディネータを返すメソッドで、コーディネータが存在しない場合は生成し、アプリケーションのストアに追加します。

最初にコーディネータのインスタンス変数persistentStoreCoordinator_の存在の有無を判別し、あればそのままモデルを返します。

コーディネータが無い場合、まず後述するapplicationDocumentsDirectoryメソッドでアプリケーションのDocumentsディレクトリを特定し、URLByAppendingPathComponent:メソッドで永続ストアファイル『Core_Data_Persistence.sqlite』が置かれている場所のURLを取得します。

次にinitWithManagedObjectModel:メソッドを用い、前述のmanagedObjectModelメソッドで得られる管理オブジェクトモデルで、コーディネータを生成・初期化します。

addPersistentStoreWithType:configuration:URL:options:error:メソッドで、特定した永続ストアファイルの場所にNSSQLiteStoreTypeの永続ストアを追加し、新たな永続ストアが生成されたらコーディネータを返し、生成に失敗した場合はNSLogでエラーを返してabort()で異常終了します。

initWithManagedObjectModel:

- (id)initWithManagedObjectModel:(NSManagedObjectModel *)model

管理オブジェクトモデルを含むレシーバを初期化します。

戻り値はmodelを含む初期化されたレシーバです。

model:管理オブジェクトモデルを指定します。

addPersistentStoreWithType:configuration:URL:options:error:

- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error

指定した場所にある指定した型の新しい永続ストアを追加し、新しいストアを返します。

戻り値は新しく生成されたストア、またはエラーが発生した場合はnilを返します。

storeType:ストア型を指定する文字列定数(例えばNSSQLiteStoreTypeなど)を指定します。
(可能な値は『Store Types』を参照してください)

configuration:新しいストアによって使用される、レシーバの管理オブジェクトモデル内の構成の名前を指定します。

storeURL:永続ストアのファイルの場所を指定します。

options:ストアを読み込みのみにするかどうかと、(XMLストアの場合は)読み込む前にXMLファイルがDTDに対して検証するかどうかを指定するキー値ペアを含む辞書を指定します。
キーの定義については『Store Options』と『Migration Options』を参照してください。
この値はnilの場合があります。

error:新しいストアが生成できない場合に、問題を説明するNSErrorのインスタンスを含めて返します。

Store Types

永続ストアの型です。

NSString * const NSSQLiteStoreType;
NSString * const NSBinaryStoreType;
NSString * const NSInMemoryStoreType;

NSSQLiteStoreType
SQLiteデータベースのストア型。

NSBinaryStoreType
バイナリのストア型。

NSInMemoryStoreType
インメモリのストア型。

6)applicationDocumentsDirectoryの追加

applicationDocumentsDirectoryは、アプリケーションのドキュメントディレクトリへのURLを返すメソッドです。

defaultManagerメソッドでシステムのファイルマネージャを取得し、URLsForDirectory:inDomains:メソッドを用いて、ユーザのホームディレクトリ(NSUserDomainMask)にあるドキュメントディレクトリ(NSDocumentDirectory)を検索し、検索結果をURLの配列で返します。

iOSの場合、アプリケーション自身のディレクトリ以外にアクセスできませんし、ドキュメントディレクトリも一つしか持たないので検索結果(の配列要素数)は一つだと思われますが、取得にはlastObjectメソッドを使用しています。

defaultManager

+ (NSFileManager *)defaultManager

ファイルシステムのデフォルトNSFileManagerオブジェクトを返します。

常にファイルマネージャの同じインスタンスを返します。

このオブジェクトはスレッドセーフではありません。

URLsForDirectory:inDomains:

- (NSArray *)URLsForDirectory:(NSSearchPathDirectory)directory inDomains:(NSSearchPathDomainMask)domainMask

要求された場所内にある指定された共有ディレクトリのURLの配列を返します。

戻り値は検索された順番のディレクトリを含むURLの配列です。

このメソッドは、システム内の既知な共有ディレクトリの場所を決定することを意図しています。

例えば、NSApplicationDirectoryへのディレクトリを設定すると、要求された場所内のアプリケーションディレクトリを返します。

NSSearchPathDirectoryで利用できる共有ディレクトリには、NSDesktopDirectoryNSApplicationSupportDirectoryなど多数あります。

directory:検索パスのディレクトリを指定します。
サポートされる値はNSSearchPathDirectoryで説明されています。

domainMask:検索を行う場所を指定します。
定数はNSSearchPathDomainMaskで指定されています。



参考文献

iOS Core Dataチュートリアル

Programming Place Plus/C言語編 第65章 終了関数

Cocoaの日々/[iOS][Mac] CoreData - マイグレーション[4] モデルファイルの構成

詳解 Objective-C 2.0 第3版詳解 Objective-C 2.0 第3版
(2011/12/28)
荻原 剛志

商品詳細を見る

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

Persistence(14)~Core Dataでの保存(1)

2011. 07. 22
手元にある旧版の『はじめてのiPhoneプログラミング』にはありませんが、新版の『はじめてのiPhone3プログラミング』には、『第11章 データの保存』にCore Dataを利用した永続ストアへの保存方法が掲載されているようですので、Apress社の原著『Beginning iPhone 3 Development Exploring the iPhone SDK』のサンプルコードと、iOS 4に対応した原著の新版『Beginning iPhone 4 Development Exploring the iOS SDK』のサンプルコードを解読して内容を確認してみます。

なお新版の書籍が手元に無いので解説内容が異なると思いますが、予めご了承ください。


●Core Dataについて

Core Dataの基礎についてはApple公式の日本語ドキュメントのページ『Apple/日本語ドキュメント』にある『iOS Core Dataチュートリアル』を参照してください。


●プロジェクトの作成

Core Dataを使用するため、Window-based Applicationテンプレートで『Use Core Data for storage』にチェックを入れ、プロジェクト名を『Core Data Persistence』とします。

7408

7409

『Use Core Data for storage』へのチェックの有無によって、生成されるプロジェクトの差異は以下の通りです。

1)クラスファイル

ヘッダファイル)
  • CoreData.hのインポート
  • 3つのインスタンス変数(管理オブジェクトコンテキスト、管理オブジェクトモデル、永続ストアコーディネータ)およびプロパティの宣言
  • 2つのインスタンスメソッド(永続ストアの保存先ディレクトリの取得と管理オブジェクトコンテキストの保存用)の宣言

ソースファイル)
  • アプリケーションがバックグラウンドになった時とアプリケーション終了時に、管理オブジェクトコンテキストを保存
  • 2つのインスタンスメソッド(永続ストアの保存先ディレクトリの取得と管理オブジェクトコンテキストの保存用)の実装
  • 3つのオブジェクト(管理オブジェクトコンテキスト、管理オブジェクトモデル、永続ストアコーディネータ)を返すインスタンスメソッドの実装
2)リソースファイル

『Resources』下に『~.xcdatamodeld』(管理オブジェクトモデルの)ファイルの追加

3)フレームワーク

『Frameworks』下にフレームワーク『CoreData.framework』の追加


●Core Dataテンプレートで追加される内容

Core Dataのテンプレートを選択することで、アプリケーションデリゲートのヘッダファイルCore_Data_PersistenceAppDelegate.hに追加される内容を見てみます。
(太字が追加された部分)

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface Core_Data_PersistenceAppDelegate : NSObject <UIApplicationDelegate> {

    UIWindow *window;

@private
    NSManagedObjectContext *managedObjectContext_;
    NSManagedObjectModel *managedObjectModel_;
    NSPersistentStoreCoordinator *persistentStoreCoordinator_;

}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (NSURL *)applicationDocumentsDirectory;

- (void)saveContext;


@end

7424

1)CoreData.hのインポート

まずCore Dataを利用するため、CoreData.hがインポートされています。

2)インスタンス変数の宣言

インスタンス変数の宣言では、@privateで管理オブジェクトコンテキスト、管理オブジェクトモデル、永続ストアコーディネータの3つが自動生成されます。

@privateは、『詳解 Objective-C 2.0』や『@IT/第3回 Objective-Cのクラス定義を理解しよう』によると、インスタンス変数の有効範囲を示すもので、変数を宣言しているクラス内(ここではCore_Data_PersistenceAppDelegate)でのみ利用できるという制限をかけていることになります。

デフォルトでは@protectedの状態で、宣言しているクラスおよびそのサブクラスで利用できます。

何故範囲制限を掛けるのかは、よく分かりません。
(ちなみにApress社のサンプルコードにおいては、iPhone OS 3.x版では@privateによる制限はされていなく、iOS 4.x版では制限がされています。Xcodeのバージョンによって自動生成される内容が異なるからかもしれません。)

NSManagedObjectContextクラスは概要でサブクラス化を非推奨しているのですが、NSManagedObjectModelクラスNSPersistentStoreCoordinatorクラスはクラスの概要で特に明記はされていないので、サブクラス化の非推奨を明示するためという訳ではなさそうです。

3)メソッドの宣言

メソッドの宣言ではウィンドウのプロパティ宣言の後で、管理オブジェクトコンテキスト、管理オブジェクトモデル、永続ストアコーディネータの3つのプロパティを宣言しています。

(自動生成の場合とiOS 4.xのサンプルコードでは)インスタンス変数とプロパティ名が異なる(インスタンス変数には最後に『_』が付いています)ことと、readonlyオプションがあることに注意してください。
(更に言えば、この3つのプロパティはソースファイルで@synthesizeによる実装を行っていません)

これまでは『インスタンス変数名 = プロパティ名』だったので違和感を感じるかもしれませんが、これは『詳解 Objective-C 2.0』によると、

・@propertyは宣言を行うだけなので、インスタンス変数で実現されているかは問わない
・@propertyで宣言したアクセサメソッドは、@synthesizeを使わずに手動で定義もできる

ということから、必ずしも『インスタンス変数名 = プロパティ名』である必要は無く(ただしアクセサメソッドを手動で定義する必要があります)、異なる名前でも問題がないということです。

自動生成されるメソッドには他に2つのインスタンスメソッドがあり、永続ストアを保存するディレクトリを取得するapplicationDocumentsDirectoryと、管理オブジェクトコンテキストを永続ストアに保存するsaveContextを宣言しています。



参考文献

iOS Core Dataチュートリアル

@IT/第3回 Objective-Cのクラス定義を理解しよう

詳解 Objective-C 2.0 第3版詳解 Objective-C 2.0 第3版
(2011/12/28)
荻原 剛志

商品詳細を見る

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る






Bose SoundLink around-ear wireless headphones II
Calendar
06 | 2011/07 | 08
Sun Mon Tue Wed Thu Fri Sat
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

Bose SoundLink around-ear wireless headphones II
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR