Core Dataによるデータの取り扱いTips。
■Appleのサンプルソース「CoreDataBooks」
登録、削除、変更を実装しているプロジェクト。
■Xcodeの新規プロジェクト作成において、Navigation-based Applicationを選択し、オプション「Use Core Data for storege」にチェックして、テンプレートから生成すると、Core Dataを使用して登録、削除ができるコードがあらかじめ組み込まれているソースが自動生成される。
■既存プロジェクトに、Core Dataを組み込む場合は、上記で示すサンプルソースか新規プロジェクトで生成したソースを組み込んで実装していけばよい。
この場合、データモデルや管理オブジェクトクラスは自分で作る必要がある。
作り方は下記URLを参照。
http://appteam.blog114.fc2.com/blog-entry-53.html
■Core Dataでアプリケーションを開発するに当たり、AppleのCore Dataプログラミングガイドの理解は必須。
http://developer.apple.com/jp/Documentation/Cocoa/Conceptual/CoreData/
■手順
1. CoreData.framewoikをプロジェクトに追加する。
2. hoge_Prefix.pch CoreDataフレームワークのヘッダファイルをインポートする。
#import <CoreData/CoreData.h>
3. グローバル変数の定義
特定のメソッドのみCoreDataを使うのであれば、サンプルソースのように特定メソッドにNSManagedObjectContextを定義して値をAppDelegateで設定すればよいが、いろいろなところでCoreDataを使う場合や今後の拡張性を考慮するのであれば、グローバル変数として定義するのがよい。
NSManagedObjectContext *managedObjectContextGlobal; // CoreData
このグローバル変数は、例えばGlobal.hという名のヘッダファイルの中に定義し、プロジェクト内の全てのメソッドのなかでこのヘッダファイルをimportしておく。
4. AppDelegate.h
@interface tsuhanManiaAppDelegate : NSObject <UIApplicationDelegate> {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSString *)applicationDocumentsDirectory;
5. AppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
managedObjectContextGlobal = self.managedObjectContext; // 追加する
}
// メソッド全部を追加する
- (void)applicationWillTerminate:(UIApplication *)application {
NSError *error;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Update to handle the error appropriately.
LOG(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
- (void)dealloc {
[managedObjectContext release]; // 追加する
[managedObjectModel release]; // 追加する
[persistentStoreCoordinator release]; // 追加する
}
// ここから下は全部追加する
#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: @"Bookmark.sqlite"]];
NSLog(@"tsuhanManiaAppDelegate persistentStoreCoordinator %@", [self applicationDocumentsDirectory]);
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible
* The schema for the persistent store is incompatible with current managed object model
Check the error message to determine what the actual problem was.
*/
// NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
// abort();
// }
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"hoge.sqlite"]; // sqliteファイルの名前は任意
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSLog(@"persistentStoreCoordinator applicationDocumentsDirectory %@", [self applicationDocumentsDirectory]);
NSLog(@"persistentStoreCoordinator storePath %@", storePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSLog(@"persistentStoreCoordinator storePath-!storePath");
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"hoge" ofType:@"sqlite"]; // sqliteファイルの名前は任意
if (defaultStorePath) {
NSLog(@"persistentStoreCoordinator defaultStorePath");
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible
* The schema for the persistent store is incompatible with current managed object model
Check the error message to determine what the actual problem was.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
#pragma mark -
#pragma mark Application's Documents directory
/**
Returns the path to the application's Documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
6. xdatamodelの作成
(1) Xcodeにて、アクション→追加→新規ファイル→iPhone OS→リソース→データモデル
(2) 拡張子xdatamodel以前のファイルの名前を入れて「次へ」をクリック。
(3) 次に表示される画面では何も入れずに保存。
(4) プロジェクト管理画面に表示された、xdatamodelファイルをクリックして開く。
(5) xdatamodelのエディタが開くので、エンテティ、プロパティを入力する。
エンテティはデータベースでいうところのテーブル、プロパティはレコードのこと。
"+"ボタンで項目が追加できる。プロパティは属性は必ず設定しておくこと。
■参考記事
http://appteam.blog114.fc2.com/blog-entry-53.html
7. データ登録、表示、削除
上記1〜6はCoreDataを扱う場合の前準備の部分。
CoreDataを使ってデータの登録、表示、削除をする場合は、Appleのサンプルソース「CoreDataBooks」または、Xcodeの新規プロジェクト作成において、Navigation-based Applicationを選択し、オプション「Use Core Data for storege」にチェックして、テンプレートから生成したソースを参照。
最低限必要なコードの例
・hogeViewController.h
@interface hogeViewController.h : UIViewController <NSFetchedResultsControllerDelegate>
{
NSFetchedResultsController *fetchedResultsController; // CoreData
NSManagedObjectContext *managedObjectContext; // CoreData
}
@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@end
・hogeViewController.m
@synthesize fetchedResultsController;
@synthesize managedObjectContext;
- (void)viewDidLoad {
// CoreData managedObjectContextをグローバル変数からメソッド変数にコピー
self.managedObjectContext = managedObjectContextGlobal;
// Set up the edit and add buttons. ナビゲーションバーに編集ボタンを追加
self.navigationItem.rightBarButtonItem = self.editButtonItem;
// CoreData Access
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
#pragma mark -
#pragma mark Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"hoge" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
■データの追加
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
[newManagedObject setValue:self.id forKey:@"id"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
■データの参照
// Configure the cell.
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
NSLog( "id %@", [[managedObject valueForKey:@"id"] description]);
■データの削除
// Delete the managed object for the given index path
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
※ 開発途中にxdatamodelの構造(プロパティの追加など)を変えると、アプリ起動時にエラーとなりアプリが落ちる。この場合は、アプリをシミュレータ、iPhone実機上から削除し、Xcodeのターゲットのクリーニング、キャッシュを空にしてから再ビルドする。このことは、AppStoreにリリース済のアプリをxdatamodelの構造変更を伴うアップデートしたときに、アプリが落ちることを意味する。
従って、xdatamodelの構造は十分検討し、後で変更の必要がないようにしておくこと。やむを得ずxdatamodelの構造変更をする場合は、AppStore上の説明において、アップデート前のアプリをアンインストールした後にインストールする旨の注意書きをしておくことが必要。
タグ:iPhone
「アプリをシミュレータ、iPhone実機上から削除し、Xcodeのターゲットのクリーニング、キャッシュを空にしてから再ビルドする。」
ここで悩んでました。
ありがとうございました。
今は
- (NSFetchedResultsController *)fetchedResultsController {
の
aFetchedResultsController.delegate = self;
がなにやってるのか調べてるところです。
自分でなんとかしたいものです。
更新楽しみにしています。
coredataのサンプルソースを探していましたら、こちらのエントリへたどり着きました。
ほぼ丸々参考にさせていただいているのですが、(汗
> managedObjectContextGlobal = self.managedObjectContext; // 追加する
> self.managedObjectContext = managedObjectContextGlobal;
がundeclaredでエラーとなってしまいました。
「managedObjectContextGlobal」の宣言が上手くできていないようで、Global.hに宣言し、各.mにてimportしているのですが、どうも定義ができていないようです。
具体的に、グローバル変数「managedObjectContextGlobal」の宣言方法をおしえていただけませんでしょうか?
#すみませんが、私objective-cが初心者でして、ご教授いただけると助かります。
"AppDelegate.m"で、"Global.h"をimportしているかご確認ください。
それでダメならビルド環境を見直してみることをおすすめします。
無事読み込めましたー。
@interfaceの中に入れてしまっていました。。。これじゃグローバル宣言にはなりませんね。ありがとうございます。
一つ質問なのですが、CoreDataを実装して見慣れないエラーに悩まされております。
Apple Mach-O Linker (Id) Error
というエラーは今回が初めてで、探しても見つからないのですが、もし何かわかるようでしたら教えて下さい。。
よろしくお願い致します。