(一)CoreData - 基本使用
@(HTML5秘籍)[Objective-c, iOS, 本地存儲]
題目雖然說的是快速入門,但是
CoreData
是一門很博大精深的技術,還是不要妄想幾天之內能融會貫通。接下來幾篇文章會由淺入深,逐步講解如何使用并掌握CoreData
。
最近一直在找一些關于日程管理的軟件,有一些感覺還不錯的,但是總覺得自己想要一些功能都沒有,所以干脆就下手自己寫一個自己的。由于沒有后臺服務器做接口,一些數據只能暫時做本地存儲。在技術選型的時候做了一些調查,因為之前做過PHP,對SQL印象蠻好的。但是對于一個移動端應用來說,基本不需要大量的存儲數據,因此在FMDB,Realm和CoreData之間,選擇了蘋果的親兒子CoreData。
[TOC]
封裝CoreData管理工具
1. 創建一個自帶CoreData代碼的工程
在Xcode創建工程的時候,就可以創建一個帶有CoreData的工程,在APPdelegate中會生成關于CoreData的代碼。
下圖是Xcode7自動創建的CoreData代碼,Xcode 8 做了修改,我們先說一下舊版的怎么玩,然后再看看新版怎么弄。
雖然說已經生成了這些代碼,但是還是不能直接用,我們需要將代碼重新封裝一下。
2. 簡單封裝CoreData管理類
① 創建一個繼承于NSObject的類 LTCoreDataManager
② 寫一個單例作為初始化方法
+ (LTCoreDataManager *)shareLTCoreDataManager
{
static LTCoreDataManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[LTCoreDataManager alloc] init];
});
return manager;
}
③ 將自動生成的代碼粘貼到 LTCoreDataManager.m 中
④ 在 LTCoreDataManager.h 中加入方法聲明
+ (LTCoreDataManager *)shareLTCoreDataManager;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
⑤ 注意:應在 LTCoreDataManager.h 中引入 <CoreData/CoreData.h>
創建CoreData模型文件
1.構建模型文件
在創建工程的時候,如果選擇了使用CoreData
,Xcode會自動生成一個模型文件,模型文件的后綴為.xcdatamodeld
。如果沒有選擇自動生成模型文件,我們可以手動創建一個模型文件Command + N,選擇 Core Data -> Data Model -> Next
。
左側有三個選項,entities
、fetch requests
、 configurations
。
2.創建實體
長按左下方的 Add Entity 按鈕,會彈出菜Add Entity
、Add Fetch Request
、Add Configuration
。選擇 Add Entity
創建一個實體,命名為Plan
。如下圖所示
右側對應著Attributes
(屬性)、Relationships
(關聯關系)、Fetched Properties
(獲取操作)。
首先,添加兩個屬性,planName
:type 為 String,planId
:type 為 Integer64。
需要注意的是,屬性的首字母要小寫。
關于Type類型的說明
- Undefined: 默認值,參與編譯會報錯
- Integer 16: 整數,表示范圍 -32768 ~ 32767
- Integer 32: 整數,表示范圍 -2147483648 ~ 2147483647
- Integer 64: 整數,表示范圍 –9223372036854775808 ~ 9223372036854775807
- Float: 小數,通過MAXFLOAT宏定義來看,最大值用科學計數法表示是 0x1.fffffep+127f
- Double: 小數,小數位比Float更精確,表示范圍更大
- String: 字符串,用NSString表示
- Boolean: 布爾值,用NSNumber表示
- Date: 時間,用NSDate表示
- Binary Data: 二進制,用NSData表示
- Transformable: OC對象,用id表示。可以在創建托管對象類文件后,手動改為對應的OC類名。使用的前提是,這個OC對象必須遵守并實現NSCoding協議。
3.添加關聯關系
再創建一個實體Task
并添加響應的屬性。
給Task
添加關聯關系,點擊Relationships
下面的加號,新建一個關聯關系,命名為plan
,inverse
需要設置好Relationships
之后才能設置。
關聯關系設置
- delete rule: 定義關聯屬性的刪除規則。在當前對象和其他對象有關聯關系時,當前對象被刪除后與之關聯對象的反應。這個參數有四個枚舉值,代碼對應著模型文件的相同選項。
- NSNoActionDeleteRule 刪除后沒有任何操作,也不會將關聯對象的關聯屬性指向nil。刪除后使用關聯對象的關聯屬性,可能會導致其他問題。
- NSNullifyDeleteRule 刪除后會將關聯對象的關聯屬性指向nil,這是默認值。
- NSCascadeDeleteRule 刪除當前對象后,會將與之關聯的對象也一并刪除。
- NSDenyDeleteRule 在刪除當前對象時,如果當前對象還指向其他關聯對象,則當前對象不能被刪除。
- Type: 主要有兩種類型,To One和To Many,表示當前關系是一對多還是一對一。
4.創建托管對象類文件
創建文件
選中后綴名為.xcdatamodeld
的模型文件,選擇Xcode
的Editor
-> Create NSManagedObject Subclass
-> 選擇模型文件
-> 選擇實體
,生成實體對應的托管對象類文件。
更新文件
當前模型對應的實體發生改變后,需要重新生成模型文件。生成步驟和上面一樣,主要是替換Category文件,托管對象文件不會被替換。生成文件時不需要刪除,直接替換文件。
增刪改查操作
1.插入操作
// 創建托管對象,并指明創建的托管對象所屬實體名
Plan *planObj = [NSEntityDescription insertNewObjectForEntityForName:@"Plan" inManagedObjectContext:context];
planObj.planName = @"計劃名字";
planObj.planId = [NSNumber numberWithInteger:1];
// 通過上下文保存對象,并在保存前判斷是否有更改
NSError *error = nil;
if (context.hasChanges) {
[context save:&error];
}
// 錯誤處理
if (error) {
NSLog(@"CoreData Insert Data Error : %@", error);
}
通過NSEntityDescription
的insert
類方法,生成并返回一個Employee
托管對象,并將這個對象插入到指定的上下文中。
managedObjectContext
將操作的數據存放在緩存層,只有調用managedObjectContext
的save
方法后,才會真正對數據庫進行操作,否則這個對象只是存在內存中,這樣做避免了頻繁的數據庫訪問。
2.刪除操作
// 建立獲取數據的請求對象,指明對Plan實體進行刪除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 創建謂詞對象,過濾出符合要求的對象,也就是要刪除的對象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"planName = %@", @"計劃名字"];
[request setPredicate:predicate];
// 執行獲取操作,找到要刪除的對象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
// 遍歷符合刪除要求的對象數組,執行刪除操作
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[context deleteObject:obj];
}];
// 保存上下文
if (context.hasChanges) {
[context save:nil];
}
// 錯誤處理
if (error) {
NSLog(@"CoreData Delete Data Error : %@", error);
}
3.更新操作
// 建立獲取數據的請求對象,并指明操作的實體為Plan
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 創建謂詞對象,設置過濾條件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"planName = %@", @"計劃名字"];
request.predicate = predicate;
// 執行獲取請求,獲取到符合要求的托管對象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.planId = @2;
}];
// 將上面的修改進行存儲
if (context.hasChanges) {
[context save:nil];
}
// 錯誤處理
if (error) {
NSLog(@"CoreData Update Data Error : %@", error);
}
4.查詢操作
// 建立獲取數據的請求對象,指明操作的實體為Plan
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 執行獲取操作,獲取所有Plan托管對象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"Plan Name : %@, Id : %@", obj.planName, obj.planId);
}];
// 錯誤處理
if (error) {
NSLog(@"CoreData Ergodic Data Error : %@", error);
}
小結一下
看完上面一大堆,其實應該還是不太理解。不過跟著步驟一步步走下來,應該是可以簡單實用了。