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 他

商品詳細を見る






Bose QuietComfort 20
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
08 | 2017/09 | 10
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
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

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

Wish List
WACOM


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

Bose QuietComfort 20
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