CoreData簡(jiǎn)述
CoreData是ios系統(tǒng)推薦給我們的數(shù)據(jù)存儲(chǔ)方案。在ios中數(shù)據(jù)存儲(chǔ)有多種方式村视,包括NSUserDefault官套、writeToFile...、NSArchiver等蚁孔,這里我主要說一下CoreData的用法奶赔。
??CoreData是數(shù)據(jù)庫(kù)嗎?很顯然不是杠氢,它是一個(gè)完整的數(shù)據(jù)處理方案站刑,封裝的是底層的SQL語(yǔ)句,所以不需要我們會(huì)寫SQL語(yǔ)句鼻百,就能夠?qū)?shù)據(jù)進(jìn)行處理绞旅,并提供了NSFetchResultsController可以將處理的結(jié)果顯示在UITableView中。但是CoreData也是有缺點(diǎn)的温艇,那就是它不如SQLite性能高因悲。
下面詳述CoreData API及其相關(guān)用法。
CoreData詳述
一勺爱、CoreData框架
CoreData是ios3.0推出的數(shù)據(jù)存儲(chǔ)方案晃琳。讓我們先看一下CoreData API 主要架構(gòu),我們可以看到該API包含了40個(gè)類(Classes)邻寿,3個(gè)協(xié)議(Protocols)蝎土,以及3個(gè)參考(References)。下面我給大家匯總绣否,一方面讓大家對(duì)CoreData整體框架有一個(gè)宏觀把握誊涯,另一方面也為深入研究做鋪墊。
???????????Tab.1 Classes Lists
Classes | Function |
---|---|
1.NSAsynchronousFetchRequest | 繼承自:NSPersistentStoreRequest |
2.NSBatchDeleteRequest | 繼承自:NSPersistentStoreRequest |
3.NSBatchUpdateRequest | 繼承自:NSPersistentStoreRequest |
4.NSSaveChangesRequest | 繼承自:NSPersistentStoreRequest |
5.NSFetchRequest | 繼承自:NSPersistentStoreRequest |
6.NSBatchDeleteResult | 繼承自:NSPersistentStoreResult |
7.NSBatchUpdateResult | 繼承自:NSPersistentStoreResult |
8.NSAsynchronousFetchResult | 繼承自:NSPersistentStoreAsynchronousResult |
9.NSPersistentStoreAsynchronousResult | 繼承自:NSPersistentStoreResult |
10.NSPersistentStoreRequest | 繼承自:NSObject |
11.NSPersistentStoreResult | 繼承自:NSObject |
12.NSAtomicStore | 繼承自:NSPersistentStore |
13.NSIncrementalStore | 繼承自:NSPersistentStore |
14.NSPersistentStore | 繼承自:NSObject |
15.NSAtomicStoreCacheNode | 繼承自:NSObject |
16.NSConstraintConflict | 繼承自:NSObject |
17.NSEntityDescription | 繼承自:NSObject |
18.NSEntityMapping | 繼承自:NSObject |
19.NSEntityMigrationPolicy | 繼承自:NSObject |
20.NSFetchedResultsController | 繼承自:NSObject |
21.NSIncrementalStoreNode | 繼承自:NSObject |
22.NSManagedObject | 繼承自:NSObject |
23.NSManagedObjectContext | 繼承自:NSObject |
24.NSManagedObjectID | 繼承自:NSObject |
25.NSManagedObjectModel | 繼承自:NSObject |
26.NSMappingModel | 繼承自:NSObject |
27.NSMergeConflict | 繼承自:NSObject |
28.NSMergePolicy | 繼承自:NSObject |
29.NSMigrationManager | 繼承自:NSObject |
30.NSPersistentContainer | 繼承自:NSObject |
31.NSPersistentStoreCoordinator | 繼承自:NSObject |
32.NSPersistentStoreDescription | 繼承自:NSObject |
33.NSExpressionDescription | 繼承自:NSPropertyDescription |
34.NSFetchedPropertyDescription | 繼承自:NSPropertyDescription |
35.NSRelationshipDescription | 繼承自:NSPropertyDescription |
36.NSAttributeDescription | 繼承自:NSPropertyDescription |
37.NSPropertyDescription | 繼承自:NSObject |
38.NSPropertyMapping | 繼承自:NSObject |
39.NSQueryGenerationToken | 繼承自:NSObject |
40.NSFetchRequestExpression | 繼承自:NSExpression |
很多的類吧蒜撮,有人看暈了嗎暴构?好吧跪呈,那我現(xiàn)在列成樹形結(jié)構(gòu),大家就清晰了取逾。
Tab.2 Protocols Lists
Protocols | Function |
---|---|
1.NSFetchedResultsControllerDelegate | 繼承自:NSObject |
2.NSFetchedResultsSectionInfo | --- |
3.NSFetchRequestResult | 繼承自:NSObject |
Tab.3 Preference Lists
Protocols | Function |
---|---|
1 | Core Data Constants |
2 | Core Data Enumerations |
3 | Core Data Data Types |
上面介紹了CoreData這個(gè)API的主要框架耗绿,具體可見CoreData官方。
二砾隅、CoreData實(shí)現(xiàn)原理
原理
CoreData存儲(chǔ)數(shù)據(jù)误阻,有幾個(gè)重要的類需要記住,這個(gè)我已經(jīng)在Tab.1中用加粗進(jìn)行顯示晴埂。NSFetchRequest究反、NSEntityDescription、NSManagedObject儒洛、NSManagedObjectContext精耐、NSManagedObjectModel。我們先看一下實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)方案的原理琅锻。
幾個(gè)重要的類
1. NSManagedObjectModel 數(shù)據(jù)模型
??可以看做是數(shù)據(jù)庫(kù)的模型結(jié)構(gòu)卦停,包含了各個(gè)實(shí)體的定義信息。有點(diǎn)像SQLite.sqlite文件恼蓬,表示一個(gè).xcdatamodeld文件惊完。
//創(chuàng)建方式
// 獲取模型文件路徑
NSURL *modelURL = [[NSBundle mainBundle] URLForResource :modelName withExtension: @"person'];
//根據(jù)模型文件創(chuàng)建模型對(duì)象
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//也可以利用下面方法,從應(yīng)用改程序包中加載.xcdatamodeld模型文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
2. NSPersistentStoreCoordinator 持久化存儲(chǔ)協(xié)調(diào)器
??它是將對(duì)象的管理部分和持久化部分捆綁在一起处硬,二者之間需要該調(diào)度器進(jìn)行調(diào)節(jié)专执。它是最接近數(shù)據(jù)底層的。用來設(shè)置CoreData存儲(chǔ)類型和存儲(chǔ)路徑郁油。
//創(chuàng)建方式:在創(chuàng)建之前必須創(chuàng)建NSManagedObjectModel模型。
NSPersistentStoreCoordinator *persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSError *error = nil;
NSString *sqlPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"person.sqlite"];
[persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL :[NSURL fileURLWithPath:sqlPath] options:nil error:&error];
if (error) {
if (fail) {
fail(error);
}
} else
{
self.context.persistentStoreCoordinator = self.persistent;
if (success) {
success();
}
}
3. NSManagedObjectContext 管理上下文
??被管理數(shù)據(jù)的上下文攀痊,實(shí)際上是對(duì)所有數(shù)據(jù)庫(kù)操作的一個(gè)緩存層桐腌,會(huì)把你所有的操作都先緩存起來避免大量磁盤IO造成不流暢,操作完成數(shù)據(jù)庫(kù)后調(diào)用save進(jìn)行持久化苟径“刚荆可以理解成用來管理.xcdatamodeld中的數(shù)據(jù)。
//創(chuàng)建
self.context = [[NSManagedObjectContext alloc] init];
NSError *error = nil;
BOOL result = [self.context save: &error];
if (!result) {
if (fail) {
fail(error);
}
} else {
if (success)
success();
}
4. NSEntityDescription
用來描述實(shí)體棘街,相當(dāng)于數(shù)據(jù)庫(kù)表中一組數(shù)據(jù)描述蟆盐。
//創(chuàng)建方式:不能用alloc init方式創(chuàng)建,通過傳入上下文和實(shí)體名稱遭殉,創(chuàng)建一個(gè)名稱對(duì)應(yīng)的實(shí)體對(duì)象(相當(dāng)于數(shù)據(jù)庫(kù)中一組數(shù)據(jù)石挂,包含各種字段)。
NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName: entityName inManagedObjectContext:self.context];
三险污、CoreData實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)
下面我們?cè)诖a層級(jí)上進(jìn)行說明CoreData的使用痹愚。
1. 新建.xcdatamodeld文件富岳。
新建完成如下所示。
2. 下面增加實(shí)體名稱拯腮,可以點(diǎn)擊person.xcdatamodeld下面的Add Entity窖式。
注意:實(shí)體名字可以修改,但是必須以大寫字母開頭动壤。
修改成功以后如下圖所示萝喘。
3. 增加實(shí)體里面的鍵值對(duì),也就是增加數(shù)據(jù)庫(kù)里面的字段琼懊。
4. 創(chuàng)建關(guān)聯(lián)類操控CoreData實(shí)體對(duì)象阁簸。
選擇要管理的數(shù)據(jù)實(shí)體模型,這里默認(rèn)給你已經(jīng)勾選好了强窖。
選擇要管理的實(shí)體,這里默認(rèn)給你已經(jīng)勾選好了削祈。
這里可以修改關(guān)聯(lián)生成文件的類型是oc還是swift翅溺,我選擇oc。
選擇person.xcdatamodeld文件髓抑,再選擇Editor-->Create NSManageObjectSubclass自動(dòng)生成四個(gè)文件咙崎。
Command + B 編譯一下。
這個(gè)結(jié)果很讓我詫異吨拍,知道是有的內(nèi)容重復(fù)了但是不知道什么原因褪猛,后來在網(wǎng)上還是找到了解決辦法。
就是把紅色框框內(nèi)部的編譯文件刪除羹饰,避免重復(fù)編譯伊滋。然后就好了。下面我們看看關(guān)聯(lián)的NSManagedObject的子類里面都是什么队秩。
PersonEntity+CoreDataClass.h文件中
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
NS_ASSUME_NONNULL_BEGIN
@interface PersonEntity : NSManagedObject
@end
NS_ASSUME_NONNULL_END
#import "PersonEntity+CoreDataProperties.h"
PersonEntity+CoreDataClass.m文件中
#import "PersonEntity+CoreDataClass.h"
@implementation PersonEntity
@end
PersonEntity+CoreDataProperties.h文件中
NS_ASSUME_NONNULL_BEGIN
@interface PersonEntity (CoreDataProperties)
+ (NSFetchRequest<PersonEntity *> *)fetchRequest;
@property (nonatomic) double weight;
@property (nullable, nonatomic, copy) NSString *name;
@property (nullable, nonatomic, copy) NSString *location;
@property (nonatomic) int16_t identify;
@property (nonatomic) BOOL gender;
@property (nonatomic) double height;
@property (nonatomic) int16_t age;
@end
NS_ASSUME_NONNULL_END
PersonEntity+CoreDataProperties.m
@implementation PersonEntity (CoreDataProperties)
+ (NSFetchRequest<PersonEntity *> *)fetchRequest {
return [[NSFetchRequest alloc] initWithEntityName:@"PersonEntity"];
}
@dynamic weight;
@dynamic name;
@dynamic location;
@dynamic identify;
@dynamic gender;
@dynamic height;
@dynamic age;
@end
注意:這里有幾點(diǎn)需要說明:
- 這四各類都繼承自NSManagedObject笑旺。
- name和location可以是nullable,表示CoreData數(shù)據(jù)庫(kù)存儲(chǔ)的對(duì)象可能為nil馍资,字段值可以為NULL筒主。
- PersonEntity+CoreDataProperties這個(gè)類不能利用alloc init方式進(jìn)行創(chuàng)建。蘋果API提供了幾個(gè)專門創(chuàng)建實(shí)體對(duì)象的方法鸟蟹。
四乌妙、CoreData實(shí)現(xiàn)數(shù)據(jù)的增刪改查
1. insert數(shù)據(jù)的增加
a) 根據(jù)Entity和Context獲取一個(gè)新的NSManagedObject
NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.context];
b) 通過setValue:forkey:給NSManagedObject對(duì)象賦值
[newEntity setValue forKey: key];
c) 保存修改
NSError *error = nil;
BOOL result = [self.context save: &error];
2. delete數(shù)據(jù)的刪除
a) 通過查詢獲得需要?jiǎng)h除的NSManagedObject, 一般用NSPredicate語(yǔ)句。
b) 在for循環(huán)中建钥,調(diào)用deleteObject:方法逐個(gè)刪除藤韵。
[self.context deleteObject: entity];
c) self.context 進(jìn)行保存。
3. update數(shù)據(jù)的修改
a) 通過查詢找到需要修改的NSManagedObject 的集合锦针,一般用NSPredicate荠察。
b) 在for循環(huán)中置蜀,調(diào)用NSManagedObject的setValue:forkey: 方法給各個(gè)屬性賦值。
c) 上下文進(jìn)行保存悉盆。
4. read數(shù)據(jù)的查詢
a) 創(chuàng)建請(qǐng)求對(duì)象盯荤。
NSFetchRequest *request = [[NSFetchRequest alloc] init];
b) 設(shè)置需要查詢的實(shí)體描述
NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext: self.context];
c) 設(shè)置查詢條件。
NSPredicate *predicate = [NSPredicate predicateWithFormat:filterString];
d) 將實(shí)體和排序都賦值給請(qǐng)求對(duì)象焕盟。
request.entity = desc;
request.predicate = predicate;
request.sortDescriptors = descArr;
e) 開始查詢
NSError *error = nil;
// NSManagedObject對(duì)象集合
NSArray *objs = [self.context executeFetchRequest:request error:&error];
// 查詢結(jié)果數(shù)目
NSUInteger count = [self.context countForFetchRequest:request error:&error];
從上面可以看到秋秤,增刪改查四種,查詢是最復(fù)雜的脚翘,而且刪除和修改也是基于查詢的灼卢,可以這么認(rèn)為查詢?nèi)绻麜?huì)了,那么增刪改查就差不多了来农。其他的就是保存等其他的操作鞋真,那就簡(jiǎn)單了。
五沃于、CoreData封裝
下面對(duì)CoreData進(jìn)行封裝涩咖。
DDCoreDataManager.h文件
#import <Foundation/Foundation.h>
#import "CoreData/CoreData.h"
#define kDDCoreDataManagerModelName (@"person")
@interface DDCoreDataManager : NSObject
@property (readonly, nonatomic, strong) NSManagedObjectContext *manageObjectContext;
@property (readonly, nonatomic, strong) NSManagedObjectModel *manageObjectModel;
@property (readonly, nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ (instancetype)shareCoreDataManager;
- (void)saveContext;
@end
DDCoreDataManager.m文件
#import "DDCoreDataManager.h"
@implementation DDCoreDataManager
@synthesize manageObjectContext = _manageObjectContext;
@synthesize manageObjectModel = _manageObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
static DDCoreDataManager *coreDataManager;
#pragma mark - 單例
+ (instancetype)shareCoreDataManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coreDataManager = [[self alloc] init];
});
return coreDataManager;
}
#pragma mark - 被管理對(duì)象模型
- (NSManagedObjectModel *)manageObjectModel
{
if (_manageObjectModel != nil) {
return _manageObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:kDDCoreDataManagerModelName withExtension:@"momd"];
_manageObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _manageObjectModel;
}
#pragma mark - document目錄
- (NSURL *)applicationDocumentDirectory
{
return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;
}
#pragma mark - 實(shí)例化調(diào)度器
- (NSPersistentStoreCoordinator *)persistentCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *coordinatorStoreURL = [[self applicationDocumentDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.sqlite",kDDCoreDataManagerModelName]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self manageObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:coordinatorStoreURL options:nil error:&error]) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = @"There was an error creating or loading the application's saved data.";
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"your error domain" code: 666 userInfo:dict];
abort();
}
return _persistentStoreCoordinator;
}
#pragma mark - 實(shí)例化上下文context
- (NSManagedObjectContext *)manageObjectContext
{
if (_manageObjectContext != nil) {
return _manageObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentCoordinator];
if (_persistentStoreCoordinator != nil) {
_manageObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_manageObjectContext.persistentStoreCoordinator = coordinator;
}
return _manageObjectContext;
}
#pragma mark - 上下文進(jìn)行保存
- (void)saveContext
{
NSManagedObjectContext *context = [self manageObjectContext];
if (context != nil) {
NSError *saveError = nil;
if ([context hasChanges] && ![context save:&saveError]) {
// 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.
NSLog(@"Unresolved error %@, %@", saveError, [saveError userInfo]);
abort();
}
}
}
@end
總結(jié)
前面對(duì)CoreData進(jìn)行了仔細(xì)的說明和封裝,有什么不對(duì)的地方還請(qǐng)大家留言和批評(píng)指正繁莹。
致謝
盡管CoreData出的較早檩互,但是使用起來總感覺沒那么順,看了不少技術(shù)大牛的博客咨演,多敲代碼和時(shí)間長(zhǎng)了才慢慢了解和熟悉闸昨。這里借鑒了很多技術(shù)大牛的經(jīng)驗(yàn),感謝薄风!