看板 Knuckles_note
作者 Knuckles (站長 那克斯)
標題 [Xcode] 使用 Core Data 儲存資料
時間 2014年09月22日 Mon. AM 02:16:20
在 iOS APP 中要儲存使用者的資料,讓 APP 關掉再打開後資料還在
若資料只是單純的 key-value 值的話,可以使用 NSUserDefaults
若是複雜的資料要用到資料庫的話,可以使用 Core Data
在 CoreData 底層是使用 SQLite 這個資料庫來儲存資料
只是把 SQL 的指令用物件導向包裝起來
在新增專案時,如果 template 是選 Empty 或 Master-Detail Application 的話
可以勾選 Use Core Data
會自動加入 CoreData.framework、
建立一個 .xcdatamodeld 檔、
以及在類別 AppDelegate 中建立一堆存取 CoreData 要用到的成員函數
如果一開始沒勾的話,可依照以下步驟自己加上去
1. 到專案設定的 General 頁,下面的 Linked Frameworks and Libraries,
加上「CoreData.framework」
編輯 Supporting Files/專案名-Prefix.pch 檔
在 #import <Foundation/Foundation.h> 這行下面加上
#import <CoreData/CoreData.h>
2. Command+n 新增檔案,選 iOS / Core Data / Data Model
Save as 的名稱可自訂,例如就輸入「CoreData」
在檔案列表就會產生一個 CoreData.xcdatamodeld 檔
3. 在 AppDelegate.h 新增三個成員變數
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
在 AppDelegate.m 的 @implementation AppDelegate 下一行加上
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
在 - applicationWillTerminate: 裡加上一行
[self saveContext];
在 APP 要被關閉前執行後面會加上的 - saveContext
在 @end 前加上以下這幾個成員函數,主要就是要用來取得上面的三個成員變數用的
===========================================================
//將有更改的資料存進資料庫
- (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 - Core Data stack
//取得 managedObjectContext
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
//取得 managedObjectModel
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
//取得 persistentStoreCoordinator
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreData.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 - Application's Documents directory
// 取得 APP 用來儲存使用者資料的資料夾路徑
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
===========================================================
以上這些是在建立專案時如果有勾使用 CoreData 的話會自動產生的
其中最重要的就是取得 managedObjectContext
透過這個就可以用 CoreData 提供的類別存取資料庫了
在其他的 ViewController 要取得 AppDelegate 裡的 managedObjectContext
可以用
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
下面的程式就直接使用 context 來存取資料庫
要在 CoreData 裡新增資料的話
建立資料結構
開啟 .xcdatamodeld 檔
建立一個 Entity,在裡面加上一組 Attributes,像這樣
Entity 就是資料庫裡的 Table,Attributes 就是 Table 的結構
這邊我們想儲存使用者的看板閱讀記錄
所以新增一個 Entity: boardHistory
Attributes 有三個分別為 bi, name, title
新增一筆資料 (INSERT)
NSManagedObject *newBoard = [NSEntityDescription insertNewObjectForEntityForName:@"BoardHistory" inManagedObjectContext:context];
[newBoard setValue:[NSNumber numberWithInt:self.bi] forKey:@"bi"];
[newBoard setValue:self.boardName forKey:@"name"];
[newBoard setValue:self.boardTitle forKey:@"title"];
在記憶體新建一筆資料,存在 NSManagedObject *newBoard
修改 newBoard 的值為想存的資料
然後執行 [context save:&error] 即可寫入資料庫
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
取得儲存的資料列表 (SELECT)
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"BoardHistory"];
NSArray *boardHistoryList = [context executeFetchRequest:request error:nil];
這樣即可將所有儲存的看板列表存成 NSMutableArray
取得的 NSMutableArray 裡存的是 NSManagedObject
例如要讀取第一筆資料的看板名稱的話,可以這樣:
NSManagedObject *board = boardHistoryList[0];
NSLog(@"board name:%@", [board valueForKey:[@"name"]);
如果要抓出特定條件的資料,要在 request 加上 NSPredicate
例如要抓出看板名為 boardName 的資料
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"BoardHistory"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", boardName];
[request setPredicate:predicate];
NSArray *boardList = [context executeFetchRequest:request error:nil];
修改資料 (UPDATE)
例如要修改某一筆資料的看板名稱
先用上面的方法將看板名為 boardName 的資料抓出來,存進 boardList
修改內容
NSManagedObject *board = boardList[0];
[board setValue:newBoardName forKey:@"name"];
然後使用 [context save:nil] 即可存進資料庫
刪除資料 (DELETE)
例如要刪除看板名稱為 boardName 的那一筆資料
先用上面的方法將看板名為 boardName 的資料抓出來,存進 boardList
接著刪除這筆資料
NSManagedObject *board = boardList[0];
[context deleteObject:board];
然後使用 [context save:nil] 即可更新資料庫
參考:
===== 問題解決記錄 ========
若是有修改 Core Data 資料結構的話,可能會在使用 Core Data 時程式就當掉
需要將本來建立的資料清除,在手機的主畫面長按APP圖示,將APP刪除後再重裝即可
--
--
※ 作者: Knuckles 時間: 2014-09-22 02:16:20
※ 編輯: Knuckles 時間: 2015-03-24 07:43:53