上面已經說完了SQLite和FMDB以及兩者的區別,本篇將講述iOS中另一個存儲方式,CoreData的使用。通讀下來大約10分鍾,后續還會根據項目中問題,不斷更新。
一、預備知識
在了解CoreData,大家有必要了解對象關系映射(英語稱object Relational Mapping,簡稱ORM)。
1.ORM
ORM是通過使用描述對象和數據庫之間映射的元數據,可以實現將對象自動持久化到關系數據庫當中。ORM的存在為了解決面向對象與數據庫科恩干存在不匹配的一種技術。
二、初識CoreData
1.CoreData是一種在iOS 3系統中,也是蘋果自己推出的數據存儲框架,采用了一種ORM(對象關系映射)的存儲關系。CoreData一個比較大的優勢在於在使用CoreData過程中不需要我們編寫SQL語句,也就是將OC對象存儲於數據庫,也可以將數據庫數據轉為OC對象(數據庫數據與OC對象相互轉換)。
2.CoreData幾個類
(1)NSManagedObjectContext
意思是托管對象上下文,數據庫的大多數操作是在這個類操作
(2)NSManagedObjectModel
意思是托管對象模型,其中一個托管對象模型關聯到一個模型文件,里面存儲着數據庫的數據結構。
(3)NSPersistentStoreCoordinator
意思是持久化存儲協調器,主要負責協調上下文玉存儲的區域的關系。
(4)NSManagedObject
意思是托管對象類,其中CoreData里面的托管對象都會繼承此類。
三、CoreData基本使用
下面開始講解CoreData的基本使用,里面會插入圖片和代碼,可能內容比較多,希望大家靜下來看完(比較考驗大家的耐心程度)。
使用CoreData方式,有兩種可能。第一種是項目開始就創建帶有CoreData數據庫,還有一種項目已經開始了,重新接入CoreData,下面我們第三部分主要講述這兩種方式的過程。
1.項目開始就使用CoreData
我們在創建項目的時候,勾選Use Core Data
如果利用項目剛建時,勾選Use Core Data,這樣在目錄中就會出現,后綴名為.xcdatamodeld。
打開AppDelegate發現類中多了以下內容
AppDelegate.h
AppDelegate.m中
我們可以點開testCoreData.xcdatamodeld文件,我們可以看到實體和關系。如下圖
通過點擊左色紅色添加紅色,右邊紅色添加屬性,在這中間我們還需要留意一些細節。
(1)
創建后可以清楚的看到模型文件左側的列表,有三個Entities、Fetch Requests以及Configurations三個選項,意思分別是:實體,請求模版以及配置信息。
(2)
添加完一個實體后,你會發現一個實體是對應着三個內容,分別是Attributes、Relationships和Fetched Properties,意思分別是:屬性、關聯關系以及獲取操作。
(3)實體屬性類型
我們來分別簡單解釋類型的意義,從上往下
Undefined:也就是默認值,如果參與編譯會報錯
Integer 16:代表整數,范圍是-32768 ~ 32767
Integer 32:代表整數,范圍是-2147483648 ~ 2147483647
Integer 64:代表整數,范圍是–9223372036854775808 ~ 9223372036854775807,還是很大的,較少用
Double:代表小數
Float:代表小數
String:代表字符串,NSString表示
Boolean:代表布爾值,使用NSNumber表示
Date:代表日期時期
Binary Data:代表二進制,是用NSData表示
Transformable:代表Objective對象,要遵守NSCoding協議
(4)關聯關系
點擊加號,可以添加關聯關系,在inverse這個屬性代表兩個實體在Relationships設置關聯關系后之后,是否可以從一個實體中找到另一個實體,這樣使兩個實體具有雙向的關聯關系。
(5)Editor Style
大家通過點擊下面紅色按鈕,style按鈕可以看出實體和屬性的關系,以及可以看出實體之間的對應的關系。
上面是coreData的視圖的基本運用,自己也是一個不斷摸索的過程,下面講述CoreData的基本操作。
三、CoreData基本使用
在講述操作之前,我們首先講述NSManagedObjectContext,蘋果推薦使用initWithConcurrencyType方式創建,在創建時,指定當前是什么類型的並發隊列,參數也是一個枚舉值。
NSManagedObjectContext枚舉值參數有三個類型:
(1)NSConfinementConcurrencyType:此類型在iOS9之后被蘋果棄用,所以不建議用這個API。
(2)NSPrivateQueueConcurrencyType:代表私有並發隊列的類型,操作也是在子線程中完成的。
(3)NSMainQueueConcurrencyType:代表主並發隊列類型,如果在操作過程中,需要涉及到UI操作,則應該使用這個參數初始化上下文完成操作。
下面我們一個company的模型文件-主隊列並發類型的NSManagedObjectContext
// 創建上下文對象,並發隊列設置為主隊列 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; // 創建托管對象模型,並使用Company.momd路徑當做初始化參數 NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"Company" withExtension:@"momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath]; // 創建持久化存儲調度器 NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 創建並關聯SQLite數據庫文件,如果已經存在則不會重復創建 NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite", @"Company"]; [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil]; // 上下文對象設置屬性為持久化存儲器 context.persistentStoreCoordinator = coordinator;
3.1 插入操作
CoreData通過NSEntityDescription的insert進行插入操作,這樣就會生成並返回一個托管對象,並將這個對象插入到上下文中。下面以一個Employee為例:
// 開始創建托管對象,並指明好創建的托管對象所屬實體名 Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context]; emp.name = @"lxz"; emp.height = @1.7; emp.brithday = [NSDate date]; // 通過這樣上下文保存對象,並在保存前判斷是否有了最新的更改 NSError *error = nil; if (context.hasChanges) { [context save:&error]; } // 錯誤處理 if (error) { NSLog(@"CoreData Insert Data Error : %@", error); }
NSManagedObjectContext將操作的數據放到了緩存層中,只有調用了NSManagedObjectContext的save后,才會對數據庫進行真正的操作,否則對象僅僅存在內存中,這樣就很好地避免了數據庫的頻繁訪問。
3.2 刪除操作
CoreData首先通過獲取需要刪除的托管對象,遍歷所需要獲取的對象數組,逐個刪除,最后調用NSManagedObjectContext的save方法。
// 獲取數據的請求對象,指明對實體進行刪除操作,以Employee為例 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 通過創建謂詞對象,然后過濾掉符合要求的對象,也就是要刪除的對象 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"]; request.predicate = predicate; // 通過執行獲取操作,找到要刪除的對象即可 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; // 開始真正操作,一一遍歷,遍歷符合刪除要求的對象數組,執行刪除操作 [employees enumerateObjectsUsingBlock:^(Employee * _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.3 修改操作
具體的描述和上面差不多。
// 獲取數據的請求對象,指明對實體進行修改操作,以Employee為例 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 創建謂詞對象,設置過濾條件,找到要修改的對象 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"]; request.predicate = predicate; // 通過執行獲取請求,獲取到符合要求的托管對象,修改即可 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { obj.height = @3.f; }]; // 通過調用save將上面的修改進行存儲 if (context.hasChanges) { [context save:nil]; } // 錯誤處理 if (error) { NSLog(@"CoreData Update Data Error : %@", error); }
3.4 查找操作
查找操作是是有許多條件限制,根據條件查找出相應的數據,下面以一個例子說明一下(查找出所有的元素,條件以后細節會講出)
// 獲取數據的請求對象,指明對實體進行查詢操作,以Employee為例 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 執行獲取操作,獲取所有Employee托管對象 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Employee Name : %@, Height : %@, Brithday : %@", obj.name, obj.height, obj.brithday); }]; // 錯誤處理 if (error) { NSLog(@"CoreData Ergodic Data Error : %@", error); }
以上就是CoreData的基本使用,自己也在不斷的完善中,希望上面對大家對CoreData認識會進一步提高。