適讀對象:
- 需要入門Core Data的朋友;
- 像我一樣,尚未學(xué)過數(shù)據(jù)庫相關(guān)課程,不太懂怎么寫SQLite語句的朋友;
- 想了解一個文科狗理解、學(xué)習(xí)Core Data心路歷程的朋友~
Core Data,iOS中一種保存和讀取數(shù)據(jù)的機制。以學(xué)習(xí)曲線陡峭而聞名~
因為我是文科狗轉(zhuǎn)行的程序猿,并沒有學(xué)過數(shù)據(jù)庫相關(guān)課程,也欣賞不出SQLite的美,所以之前的項目一直用NSKeyedArchiver和NSKeyedUnarchiver(固化)進行數(shù)據(jù)的本地保存(所幸我接觸的項目,數(shù)據(jù)都不會太復(fù)雜)。
其實一開始接觸iOS開發(fā),就有閱讀過Core Data相關(guān)內(nèi)容。不過一來當(dāng)時水平太低,看不太懂;二來Core Data本來也難學(xué);三來經(jīng)手的項目也沒有強制使用Core Data;四來國內(nèi)使用Core Data的開發(fā)者也不占主流。所以花了很長很長一段時間才入了門。過程算是曲折,所以標(biāo)題嘩眾取寵地用了「死磕」二字。
「太長不看版」
本文確實比較長(從側(cè)面印證Core Data內(nèi)容確實多),所以這里寫一個「太長不看版」,「以饗讀者」:
Core Data使用流程:
- 創(chuàng)建Core Data Stack
- iOS10中利用NSPersistentContainer
- iOS10之前涉及NSManagedObjectContext、NSPersistentStoreCoordinator、NSManagedObjectModel、NSPersistentStore這些類
- 創(chuàng)建模型文件(.xcdatamodel文件)
- 按需增加「實體」、實體的「特性」、「關(guān)系」(如有需要)
- 創(chuàng)建NSManagedObject子類(如有需要)
備注:如果創(chuàng)建項目時勾選了「Use Core Data」,會自動幫你創(chuàng)建好上述這些內(nèi)容。
- 增刪查改:
-
增:利用NSEntityDescription的類方法
insertNewObjectForEntityForName:inManagedObjectContext:
,添加數(shù)據(jù)(對象) -
刪:利用NSManagedObjectContext的
deleteObject:
方法刪除數(shù)據(jù) -
查:利用NSManagedObjectContext的
executeFetchRequest:error:
方法,查詢數(shù)據(jù) -
保存:利用NSManagedObjectContext的
save:
方法保存數(shù)據(jù)。
OK,基本上就是這些東西了~
術(shù)語
CoreData學(xué)習(xí)曲線陡峭的原因之一,術(shù)語太多算一個。所以這里整理一下,如下:

Core Data Stack
感覺理解起來有點抽象,先看官方定義:
The Core Data stack is a collection of framework objects that are accessed as part of the initialization of Core Data and that mediate between the objects in your application and external data stores.
說是一個對象的集合,由4個主要對象構(gòu)成:
- 「managed object context」 (NSManagedObjectContext),
- 「persistent store coordinator」 (NSPersistentStoreCoordinator),
- 「managed object model」 (NSManagedObjectModel),
- 「persistent container」 (NSPersistentContainer).
我是這樣理解的:Core Data Stack,就是進行數(shù)據(jù)增刪查改、保存的「工作臺」,Apple提供這樣一個「工作臺」,讓你方便進行數(shù)據(jù)的保存。無需關(guān)心實現(xiàn)細(xì)節(jié)。
對應(yīng)示意圖第1個框框。
Persistent Container
NSPersistentContainer是iOS 10、 macOS 10.12之后才出現(xiàn)的新類。引入這個新類的目的之一,就是為了簡化創(chuàng)建Core Data Stack這個工作臺的過程。所以,在iOS10之前,創(chuàng)建Core Data Stack會復(fù)雜一些。
而Persistent Container也有另一個新類NSPersistentStoreDescription,可以利用這個類,進行一些定制化設(shè)置,比如自定義存儲路徑、設(shè)置存儲數(shù)據(jù)方式等(Core Data支持SQLite、XML、Binary、InMemory 4中方式存儲數(shù)據(jù))。
備注:iOS10中,如果利用NSPersistentContainer創(chuàng)建Core Data Stack,預(yù)設(shè)的是NSSQLiteStoreType類型。并且默認(rèn)打開了自動輕量化版本遷移功能(換言之,在iOS10之前,需要手動進行相關(guān)設(shè)置,才能打開版本遷移功能)。
對應(yīng)示意圖第2個虛線框框。
Managed Object Context。
可以理解為是一塊內(nèi)存,提供了和Managed Objects交互的場所。也稱為:The Context或者MOC。NSManagedObjectContext類實例。
備注:對數(shù)據(jù)進行刪除、保存、查詢,都要用到NSManagedObjectContext類的相關(guān)方法。
對應(yīng)示意圖第3個框框。
Managed Object Model
直觀點,你可以把它理解為就是Xcode中后綴為xcdatamodel的文件。在這個文件里,你可以通過非代碼、可視化的方式,定義對象、對象的屬性、對象之間的關(guān)系(Core Data把對象稱呼為「實體」、對象的屬性稱呼為「特性」)。
Managed Object Model,就是Core Data中用于描述實體、實體特性、實體間關(guān)系的一套方案。
它是NSManagenObjectModel的類實例(也可以通過純代碼實現(xiàn).xcdatamodel文件的內(nèi)容)。也稱為:The Model, Data Model, Schema或Object Graph。
換言之,Managed Object Model定義了你App的整個數(shù)據(jù)結(jié)構(gòu)。
下面3個,是在設(shè)置.xcdatamodel文件時會遇到的3個術(shù)語。
- Entity /「實體」
NSEntityDescription類實例,用于定義一個對象。一個「實體」,最少要有「名字」和「類名」(如果沒有設(shè)置類名,默認(rèn)是NSManagedObject類)。
- Attribute / 特性
「實體特性」。NSAttributeDescription類實例。就是Entity的特性,對應(yīng)App中的創(chuàng)建類時的屬性。
- Relationship / 關(guān)系
「實體關(guān)系」。 NSRelationshipDescription類實例。用于描述Entity之間的關(guān)系。
對應(yīng)示意圖第4個框框。
Managed Object。
就是需要保存的數(shù)據(jù),是NSManagenObject類實例。(對應(yīng)App中的「對象」)
就我的理解,Managed Object和上面提到的Entity,本質(zhì)上是同一個東西,就是你的數(shù)據(jù)對象,只不過是在可視化操作和純代碼操作中的不同稱謂。
對應(yīng)示意圖第5的那些框框。
Persistent Store Coordinator
協(xié)調(diào)Context和Persistent Store的一個角色。NSPersistentStoreCoordinator類實例。
如果只是對數(shù)據(jù)進行簡單的增刪查改,我們并不需要接觸到這個類。
對應(yīng)示意圖第6個框框。
Persistent Store
可以理解為保存數(shù)據(jù)的地方。用于設(shè)置保存數(shù)據(jù)的方式、以及保存的路徑等。(保存數(shù)據(jù)的方式指SQLite、XML、Binary、InMemory4種)。NSPersistentStore類實例。也稱為The Store或者Database。
在iOS10之前,如果需要支持版本遷移功能,需要在創(chuàng)建NSPersistentStore類實例時,傳入相應(yīng)的options參數(shù)。而在iOS10中,則會自動打開版本遷移功能,并默認(rèn)設(shè)置數(shù)據(jù)類型為NSSQLiteStoreType(見上面的名詞:「NSPersistentStoreDescription」)。
「版本遷移」,一開始對這個名字很是迷惑,還以為是將數(shù)據(jù)模型從一個App遷移到另外一個App。其實,是在內(nèi)部進行「遷移」。
簡單說,假如修改了數(shù)據(jù)模型(比如修改了. xcdatamodel文件:增加了實體,增加了特性等等),為了防止使用者在更新App后,由于數(shù)據(jù)模型不一致導(dǎo)致崩潰,需要進行一定的處理,這個處理,他們叫「版本遷移」(叫「版本升級」不是更合適嗎~)。
對應(yīng)示意圖第7個框框。
其他
Optional:「實體特性」的配置選項(勾選了之后,表示這個特征可為空nil)
Transient:「實體特性」的配置選項(勾選了之后,該屬性不會保存到沙盒中)
Fetch Requset。描述了從Persistent Store中取回數(shù)據(jù)的方式方法。NSFetchRequest類實例。查詢數(shù)據(jù)的時候會用到。
Preficate:又稱為:Filter。描述了取回數(shù)據(jù)的過濾方式。(有人翻譯為「斷言」,有人翻譯為「謂語」)。NSPredicate類實例。查詢數(shù)據(jù)的時候會用到。
Stort Descriptor。描述了取回數(shù)據(jù)的排序方式。NSSortDescriptor類實例。也是查詢數(shù)據(jù)的時候會用到。
可參考以下表格,對照進行理解(這個表格或許不慎嚴(yán)謹(jǐn))
數(shù)據(jù)庫術(shù)語 | 代碼中的術(shù)語 | Core Data中的術(shù)語 |
---|---|---|
表格 | 類 | 實體 / Entity(NSEntityDescription類實例) |
列 | 屬性 | 實體特性(Attribute) |
行 | 對象(類實例) | NSManagedObject(子)類實例 |
使用步驟
大部分教程是先創(chuàng)建「managed object model」,再初始化「Core Data Stack」的。因為我這里把「Core Data Stack」比喻成「工作臺」,所以這篇文章先進行「Core Data Stack」的初始化。
1、初始化Core Data Stack
上面我們將Core Data Stack比喻成一個「工作臺」,是一切操作的所在地。
不過由于iOS10新引進了NSPersistentContainer類,然后新建項目又可以選擇勾選Core Data與否。所以情況變得稍稍有點復(fù)雜。
這里分三種情況:1、在既有項目(只需支持iOS10)初始化Core Data Stack;2、在既有項目(需兼容iOS8、9、10等系統(tǒng))初始化Core Data Stack;3、新建項目時直接勾選了Core Data。
情況1:在既有項目添加Core Data功能(只需支持iOS10)
由于iOS10引進了NSPersistentContainer,如果單單只支持iOS10系統(tǒng),初始化Core Data Stack相比以前簡單很多。
// 我們先聲明了一個NSPersistentContainer類型的屬性:persistentContainer,在適合的時間調(diào)用initWithName:對其初始化
// 這里的Name參數(shù),需要和后續(xù)創(chuàng)建的.xcdatamodeld模型文件名稱一致。
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"MoveBand"];
// 調(diào)用loadPersistentStoresWithCompletionHandler:方法,完成Core Data Stack的最中初始化。
// 如果不能初始化成功,在Block回調(diào)中打印錯誤,方便調(diào)試
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription * _Nonnull description, NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Fail to load Core Data Stack : %@", error);
abort();
}
else {
...
}
}];
就兩步:
- 初始化NSPersistentContainer對象;
- 調(diào)用NSPersistentContainer的loadPersistentStoresWithCompletionHandler:完成初始化。
更詳細(xì)的說明,可參考官方文檔Initializing the Core Data Stack
備注:你可以仿照Xcode所創(chuàng)建的模版,直接在AppDelegate類中橋敲以上代碼。也可以新建一個專門負(fù)責(zé)儲存功能的類,在這個類中敲這段代碼。(我一般不喜歡將這部分代碼放在AppDelegate類中,所以我創(chuàng)建工程的時候,都不會勾選Use Core Data)。
情況2:在既有項目初始化Core Data Stack(需兼容iOS8、9、10等系統(tǒng))
因為NSPersistentContainer不兼容iOS10之前的系統(tǒng)。所以,如果你已經(jīng)用了NSPersistentContainer初始化了Core Data Stack,但同時也要兼容iOS8、9等系統(tǒng),就需要在代碼中檢查,如果是舊的系統(tǒng),就需要用舊的方法初始化Core Data Stack了。示例如下:
- (instancetype)init
{
self = [super init];
if (self) {
NSInteger majorVersion = [NSProcessInfo processInfo].operatingSystemVersion.majorVersion;
if (majorVersion < 10) {
// iOS10以下的系統(tǒng), 用舊有的方法初始化Core Data Stack
[self initializeCoreDataLessThaniOS10];
}
else {
// iOS10的系統(tǒng), 用新的方法(詳見上面介紹的情況1)
[self initializeCoreData];
}
}
return self;
}
- (void)initializeCoreDataLessThaniOS10 {
// Get managed object model(拿到模型文件,也就是.xcdatamodeld文件(我們會在初始化完Core data Stack后創(chuàng)建))
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MoveBand" withExtension:@"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(mom != nil, @"Error initalizing Managed Object Model");
// Create persistent store coordinator(創(chuàng)建NSPersistentStoreCoordinator對象(需要傳入上述創(chuàng)建的NSManagedObjectModel對象))
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
// Creat managed object context(創(chuàng)建NSManagedObjectContext對象(_context是聲明在.h文件的屬性——因為其他類也要用到這個屬性))
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// assgin persistent store coordinator(賦值persistentStoreCoordinator)
_context.persistentStoreCoordinator = psc;
// Create .sqlite file(在沙盒中創(chuàng)建.sqlite文件)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];
// Create persistent store(異步創(chuàng)建NSPersistentStore并add到NSPersistentStoreCoordinator對象中,作用是設(shè)置保存的數(shù)據(jù)類型(NSSQLiteStoreType)、保存路徑、是否支持版本遷移等)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 用于支持版本遷移的參數(shù)
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = _context.persistentStoreCoordinator;
// 備注,如果options參數(shù)傳nil,表示不支持版本遷移
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error];
NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
});
}
可以看到,舊方法初始化Core Data Stack還是比較麻煩的。
當(dāng)然,如果你不想做這個判斷,只用上面方法初始化即可,這個方法在新舊系統(tǒng)都正常工作。
情況3:直接勾選Core Data
創(chuàng)建項目時,如果直接勾選Core Data復(fù)選框,項目模版會在AppDelegate類中直接幫你初始化好Core Data Stack,自動創(chuàng)建和上面情況1類似的代碼(Xcode8)
在AppDelegate.h文件
#import <UIKit/UIKit.h>
// 導(dǎo)入了CoreData框架
#import <CoreData/CoreData.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
// 在.h文件聲明一個NSPersistentContainer類型的屬性(為了讓其他類可以調(diào)用)
@property (readonly, strong) NSPersistentContainer *persistentContainer;
// 聲明了一個保存數(shù)據(jù)的方法
- (void)saveContext;
@end
在AppDelegate.m文件
@implementation AppDelegate
……
#pragma mark - Core Data stack
@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
@synchronized (self) {
if (_persistentContainer == nil) {
// 實例化NSPersistentContainer對象。
// 注意:參數(shù)傳入的名稱,就是.xcdatamodeld文件名稱(兩者需要一直)(勾選Core Data后,會自動創(chuàng)建一個.xcdatamodeld文件)
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataTestUseCoreData"];
// 加載persistent stores,實現(xiàn)最終的Core Data stack的創(chuàng)建
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
// 如果有錯誤,打印出來
if (error != nil) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
// 注意這句,NSManagedObjectContext對象,是通過上面創(chuàng)建的NSPersistentContainer對象的屬性viewContext獲取的,無需自己初始化(iOS10之前要自己初始化)
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
// 保存數(shù)據(jù),直接用的是NSManagedObjectContext的save:方法,很簡單。
if ([context hasChanges] && ![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}
系統(tǒng)幫我們創(chuàng)建了一個NSPersistentContainer實例,以及一個saveContext方法。(并且已經(jīng)幫我們創(chuàng)建了.xcdatamodeld模型文件)
注意看saveContext,我們通過NSPersistentContainer的屬性viewContext拿到NSManagedObjectContext對象,再通過save:方法進行數(shù)據(jù)的保存。
因為系統(tǒng)并沒有幫我們適配舊系統(tǒng),所以如果App要在非iOS10的舊系統(tǒng)運行,還需要做類似情況2的工作。
如果是Xcode8之前的版本自動創(chuàng)建的Core Data Stack,會不一樣(跟情況2類似),這里不再贅述。
2、創(chuàng)建「managed object model」
好了,有了「工作臺」,接著就需要「材料」了。也就是你要保存什么東西,這些東西有什么特性,這些東西之間有什么關(guān)系……Xcode提供了一套可視化的方案讓我們「描述」這部分內(nèi)容。
創(chuàng)建xcdatamodeld文件
快捷鍵:Command + N,選擇Core Data欄目下的「Data Model」,就可以創(chuàng)建一個.xcdatamodeld模型文件(managed object model),名字隨意。接著我們就可以往里面添加材料了。
添加實體、實體的特性、關(guān)系
這部分用一張圖概括:

坑:這里有個坑,在Xcode8中,Codegen下拉選擇框中增加了Class/Definition這一選項,而且是默認(rèn)的預(yù)設(shè)值,這時候系統(tǒng)會自動幫我們這個實體創(chuàng)建了NSManagedObject子類,最坑的是,這些自動創(chuàng)建的類,在導(dǎo)航面板是看不見的!!!然后你很容易再重復(fù)手動創(chuàng)建NSManagedObject子類,這時候就會報類似「duplicate symbol _OBJC_METACLASS_Photography in:...」這類錯誤。
所以,如果你想自己手動創(chuàng)建NSManagedObject子類,就要把系統(tǒng)預(yù)設(shè)的Class/Definition改為Manual/None。
創(chuàng)建NSManagedObject子類
好了,通過上面的一步,我們知道我們要保存的是什么東西,以及知道他們是什么關(guān)系了(數(shù)據(jù)模型建好了)。
為什么要用NSManagedObject子類
這時候其實可以進行數(shù)據(jù)的增刪查改了。但是這時候賦值(或者修改)一條數(shù)據(jù),都是通過NSManagedObject類實例進行的(我們創(chuàng)建的實體,都是NSManagedObject類型的),類似如下:
NSManagedObject *newUser = …… // 這里聚焦在數(shù)據(jù)的賦值與取值, 暫時省略插入一條數(shù)據(jù)的方法
// 賦值
[newUser setValue:@"Antony" forKey:@"name"];
[newUser setValue:@123 forKey:@"userID"];
// 取值
NSManagedObject *selectedUser = ……
NSString *name = [selectedUser valueForKey@"name"];
……
以上的存取值方式,有點類似字典。不直觀,敲字符串也容易出錯。所以,我們通常都會創(chuàng)建NSManagedObject的子類,用點語法直接進行存取操作。
在.h文件
#import <CoreData/CoreData.h>
@interface SPKUser : NSManagedObject
@property (copy, nonatomic) NSString *name;
@property (nonatomic) int64_t userID;
@end
在.m文件
#import "SPKUser.h"
@implementation SPKUser
// 在OC中,將某個屬性實現(xiàn)為@dynamic,表示編譯器在編譯時不會對這個屬性的存取方法(getter/setter)做檢查(由程序員自己提供存取方法)。在Core Data中,由Core Data實現(xiàn)。
@dynamic name;
@dynamic userID;
@end
然后就可以這樣:
- (void)addNewUser {
SPKUser *newUser = ……;
newUser.name = @"Antony";
newUser.userID = 123;
NSLog(@"添加了一個user");
}
所以,這就是應(yīng)用NSManagedObject子類的好處。
如何創(chuàng)建NSManagedObject子類
創(chuàng)建NSManagedObject子類,有如下兩種辦法
- 方法1:直接Command + N創(chuàng)建一個新類,繼承NSManagedObject類,然后定義的屬性和模型文件中的一致。
- 方法2:選中對應(yīng)的實體,然后Editor > Create NSManagedObject Subclass...,系統(tǒng)自動生成NSManagedObject子類。
這種方法,如果有「對多」的關(guān)系,會生成2個Category(Core Data生成的NSManagedObject子類,都是以Category形式存在的) - CoreDataProperties:生成實體中Attributes對應(yīng)的屬性。Relationships也會生成對應(yīng)的屬性:「對多」關(guān)系是NSSet/NSOrderSet類型屬性(本質(zhì)是個集合),「對一」關(guān)系則是非集合的對象類型屬性。
- CoreDataGeneratedAccessors——其實就是一系列增加、刪除NSOrderSet/NSSet里元素的方法。(如果沒有對多關(guān)系,不會有這個Category)
注意,第二種方式創(chuàng)建NSManagedObject子類,默認(rèn)語言是Swift,如果需要改為OC,則到「File inspector」中修改,如下:

3、增
好啦,有了「工作臺」(Core Data Stack),又有了「材料」(managed object model),可以擼起袖子干了……(第一張示意圖,其實都有對增刪查、保存方法有所提及)
- (void)addNewUser {
SPKUser *newUser = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:_context];
newUser.name = @"Antony";
newUser.userID = 123;
}
看以上代碼,增加一條數(shù)據(jù),并不是調(diào)用NSManagedObjectContext類中的某個方法,而是用了NSEntityDescription的類方法insertNewObjectForEntityForName:inManagedObjectContext:
,第一個參數(shù)傳入實體名稱,第二個參數(shù)傳入context(因為Core Data是支持多個context的,所以這里傳入context參數(shù)以界定是在哪個context中操作)。
該方法會返回一個NSManagedObject,或其子類的對象,然后就可以對該對象進行賦值操作了。
注意:此時數(shù)據(jù)只存在內(nèi)存中,并沒有固化、保存到沙盒。還需要通過特定的保存方法才能固化到沙盒。
另外,不能用alloc、init方法創(chuàng)建一個新的對象,會崩潰。
4、刪
刪除數(shù)據(jù)比較簡單,直接調(diào)用NSManagedObjectContext的deleteObject:
方法即可。當(dāng)然,要怎么獲取所要刪除的對象,就自己斟酌了,可以通過NSFetchRequest查詢獲取要刪除的對象,也可以用NSFetchedResultsController的objectAtIndexPath:方法拿到要刪除的對象(NSFetchedResultsController另一篇文章再介紹)
- (void)removeUser:(SPKUser *)user {
[_context deleteObject:user];
}
5、查(Fetching Objects)
查詢功能,是被官方特別強調(diào)的一個功能,據(jù)聞可以玩出很多花樣兒~
- (NSArray *)allUsers {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
NSError *error = nil;
NSArray *results = [_context executeFetchRequest:request error:&error];
if (!results) {
NSLog(@"Error fetching Employee objects: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
}
return results;
}
上面是一個最簡單的查詢,調(diào)用NSManagedObjectContext的 executeFetchRequest:error:
方法,傳入一個NSFetchRequest對象作為參數(shù),這個參數(shù)定義了要取回的是哪個實體。
另外,還可以通過NSPredicate(「謂語」,也有翻譯為「斷言」的)進行數(shù)據(jù)篩選,只獲取某些符合條件的數(shù)據(jù)。還可以通過NSSortDescriptor設(shè)置獲取數(shù)據(jù)的排列順序。如下:
- (NSArray *)allUsers {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
// 只取回firstName是Antony的數(shù)據(jù)
NSString *firstName = @"Antony";
[request setPredicate:[NSPredicate predicateWithFormat:@"firstName == %@", firstName]];
// 取回的數(shù)據(jù)按userID進行由小到大(升序)的排序
NSSortDescriptor *userIDSort = [NSSortDescriptor sortDescriptorWithKey:@"userID" ascending:YES];
// 注意,這個參數(shù)是一個數(shù)組,所以排序可以有多個條件,比如先按身高從低到高排,滿足此條件后再按照名字首字母A~Z從前到后排。這時候,身高的Sort Descriptor放在數(shù)組前面,名字的Sort Descriptor放在數(shù)組后面。
[request setSortDescriptors:@[userIDSort]];
NSError *error = nil;
NSArray *results = [_context executeFetchRequest:request error:&error];
if (!results) {
NSLog(@"Error fetching Employee objects: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
}
return results;
}
關(guān)于NSPredicate更詳細(xì)的用法,可參考官方文檔:Predicate Programming Guide
6、改
修改數(shù)據(jù),和上面的增加一條數(shù)據(jù)的情況比較相似,直接對屬性進行修改。先查詢到你要的數(shù)據(jù)對象,再重新賦值即可。
如果要大批量修改數(shù)據(jù),將數(shù)據(jù)從沙盒加載到內(nèi)存,再進行修改,不利于性能,所以可以使用NSBatchUpdateRequest 、NSBatchDeleteRequest,進行批量的修改或者刪除。這種方法直接在數(shù)據(jù)庫內(nèi)完成,無需加載到內(nèi)存,利于性能提升。(但進行批處理后,因為操作是在數(shù)據(jù)庫中完成的,要注意合并更新到Context中,以保持兩者一致)
關(guān)于批處理,可以參考《New in Core Data and iOS 8: Batch Updating》,這里不再展開( 其實我自己暫時也沒用過:D )
7、保存
保存比較簡單,直接調(diào)用NSManagedObjectContext的save:
方法即可,如下:
- (void)save {
NSError *error = nil;
if ([_context save:&error] == NO) {
NSAssert(NO, @"Error saving context %@\n%@", [error localizedDescription], [error userInfo]);
}
}
也可以調(diào)用NSManagedObjectContext的hasChanges
方法,來判斷:在數(shù)據(jù)有變化的情況下再調(diào)用save:
方法。
注意:在調(diào)用save方法之前,上面做的所有操作(增、刪、改),都只是保存在內(nèi)存中,并不會固化到沙盒中。
版本「遷移」
應(yīng)用場景:修改了數(shù)據(jù)結(jié)構(gòu)(比如說某個實體增加了一個特性),這時候就要進行版本遷移了,否則已經(jīng)安裝舊App的手機,在更新應(yīng)用后,兩邊數(shù)據(jù)結(jié)構(gòu)不一致導(dǎo)致不能識別,會崩潰。
步驟:
- 選中.xcdatamodeld文件,Editor > Add Model Version,創(chuàng)建一個新版的.xcdatamodeld文件
-
切換到新版的.xcdatamodeld文件(切換成功后會有綠色的勾),如下圖:
切換到新版的.xcdatamodeld文件 - 對.xcdatamodeld文件進行你想要的修改
- 創(chuàng)建NSPersistentStore的時候,options參數(shù)傳一個dictionary,值如下:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
更詳細(xì)的代碼,上面用舊方法創(chuàng)建Core Data Stack時也有涉及。
大家也可以自己驗證一下,不進行版本遷移,直接修改.xcdatamodeld文件,然后運行程序,會報什么錯。
以上是自動、輕量化的版本遷移,至于更復(fù)雜的版本遷移,我目前也沒有接觸到,不再展開。可以參考:
Core Data Model Versioning and Data Migration Programming Guide
End
《認(rèn)識CoreData-初識CoreData》系列文章,寫得很詳細(xì),推薦閱讀。
以上就是Core Data的入門用法(文科狗,不容易啊 XD )。