iOS CoreData介紹和使用(以及一些注意事項(xiàng))
最近花了一點(diǎn)時(shí)間整理了一下CoreData,對(duì)于經(jīng)常使用SQLite的我來說,用這個(gè)真的有點(diǎn)用不慣,個(gè)人覺得實(shí)在是沒發(fā)現(xiàn)什么亮點(diǎn),不喜勿噴啊。不過這門技術(shù)的出現(xiàn)也有其存在價(jià)值,這是不可否認(rèn)的事實(shí),即使是不喜歡我們也得去了解一下,因?yàn)槟悴挥脛e人會(huì)用,這年頭都多人開發(fā),多學(xué)點(diǎn)還是有好處的。廢話不多說了,該開始正經(jīng)事了。
CoreData介紹
CoreData是一個(gè)模型層的技術(shù),也是一種持久化技術(shù)(數(shù)據(jù)庫),它能將模型對(duì)象的狀態(tài)持久化到磁盤里,我們不需要使用SQL語句就能對(duì)它進(jìn)行操作。不過在性能方面是弱于直接使用SQLite的。
CoreData使用
理論我就不多說了,覺得我說的不夠詳細(xì)的可以自行搜索關(guān)于更多的CoreData介紹。
- 創(chuàng)建步驟流程
- 第一步先創(chuàng)建.xcdatamodeld文件(New File -> iOS -> Core Data ->Data Model)
名字雖然可以任意取,但最好還是取和自己存儲(chǔ)數(shù)據(jù)庫名字一樣的名字。這樣可讀性更高些。(ps:這個(gè)文件就相當(dāng)于數(shù)據(jù)庫文件一樣,數(shù)據(jù)庫文件中可以有多個(gè)表,表中可以有各個(gè)字段值,這里也可以有多個(gè)實(shí)體,每個(gè)實(shí)體有各個(gè)鍵值)
通過上面的操作就可以創(chuàng)建一個(gè).xcdatamodeld文件,現(xiàn)在我們點(diǎn)擊這個(gè)文件,在最下面找到Add Entity按鈕,進(jìn)行實(shí)體添加。
現(xiàn)在我們添加了一個(gè)名字為Entity的實(shí)體,這個(gè)名字是默認(rèn)名字,現(xiàn)在我們可以對(duì)他進(jìn)行名字的更改,對(duì)它雙擊一下即可修改名字,或者在xcode右邊的信息欄中也可以修改
通過上面操作我們已經(jīng)創(chuàng)建好了實(shí)體的名字,現(xiàn)在我們需要往實(shí)體中添加我們需要的鍵值。(ps:相當(dāng)于數(shù)據(jù)庫表中的字段),具體操作看圖說話(實(shí)體之間的關(guān)聯(lián)我就不介紹了,有興趣的可以自行搜索資料,個(gè)人覺得會(huì)了單個(gè)實(shí)體創(chuàng)建外加查詢就行,關(guān)聯(lián)也無非是找到關(guān)聯(lián)的實(shí)體中共同的鍵值從而取得另外一個(gè)實(shí)體對(duì)象,我們可以直接先從一個(gè)實(shí)體取得指定鍵值對(duì)應(yīng)的屬性,再通過屬性值去查詢另一個(gè)實(shí)體,只是沒關(guān)聯(lián)的那么方便而已)。
- 第二步創(chuàng)建關(guān)聯(lián)類來操控CoreData實(shí)體對(duì)象(選中.xcdatamodeld文件->xcode菜單欄->Edit->Create NSManagedObject Subclass)
選中自己需要關(guān)聯(lián)的.xcdatamodeld文件名稱,點(diǎn)擊下一步即可。
選中.xcdatamodeld文件中需要關(guān)聯(lián)的實(shí)體對(duì)象,點(diǎn)擊下一步然后在選擇存儲(chǔ)目錄即可。
完成后會(huì)發(fā)現(xiàn)自動(dòng)生成了實(shí)體名稱對(duì)應(yīng)的類和擴(kuò)展類(Entity.h/.m和Entity+CoreDataProperties.h/.m)
到這里就完成了整一個(gè)創(chuàng)建的流程,個(gè)人覺得說的有點(diǎn)過于詳細(xì)了(導(dǎo)致看起來有點(diǎn)啰嗦)
- 注意
我在試驗(yàn)的過程中發(fā)現(xiàn),如果我把關(guān)聯(lián)的類從項(xiàng)目中刪除,在對(duì)實(shí)體名稱再次修改后,重新創(chuàng)建關(guān)聯(lián)類,發(fā)現(xiàn)類名還是原來的實(shí)體名稱顯示,可能xcode沒刷新,反正最后我是刪除實(shí)體重新Add Entity之后才有用,所以為了不必要麻煩,最好還是想好實(shí)體名稱后再創(chuàng)建關(guān)聯(lián)類。
- 關(guān)聯(lián)類的理解
我就以我自己創(chuàng)建的類來說明
#import "UploadEntity.h"
NS_ASSUME_NONNULL_BEGIN
@interface UploadEntity (CoreDataProperties)
@property (nullable, nonatomic, retain) NSString *fileName;
@property (nullable, nonatomic, retain) NSString *fileSize;
@property (nullable, nonatomic, retain) NSNumber *fileType;
@property (nullable, nonatomic, retain) NSNumber *finishStatus;
@property (nullable, nonatomic, retain) NSData *imageData;
@property (nullable, nonatomic, retain) NSNumber *time;
@property (nullable, nonatomic, retain) NSString *urlPath;
@end
NS_ASSUME_NONNULL_END
該類繼承NSManagedObject類
可以看到在實(shí)體中的每一個(gè)鍵值都被xcode自動(dòng)生成了實(shí)體類的對(duì)象屬性,而且對(duì)于基本數(shù)據(jù)類型被自動(dòng)轉(zhuǎn)成了NSNumber。
每個(gè)對(duì)象的修飾詞都有nullable,表示CoreData數(shù)據(jù)庫存儲(chǔ)的對(duì)象可能為nil(允許存儲(chǔ)nil,就如數(shù)據(jù)庫一樣,字段值也能為NULL),也就是說我們以后從CoreData讀取的數(shù)據(jù)是有可能為nil。
這個(gè)類對(duì)象的創(chuàng)建我們不能用以往的[[NSObject alloc] init]方式去創(chuàng)建,因?yàn)榻?jīng)過驗(yàn)證直接這樣創(chuàng)建它,然后對(duì)屬性賦值會(huì)直接拋出異常,估計(jì)內(nèi)部經(jīng)行了斷言操作吧。蘋果API提供了幾個(gè)專門創(chuàng)建實(shí)體對(duì)象的方法,后面我在講。
因?yàn)楦鞣N限制的原因,比如創(chuàng)建對(duì)象限制等,導(dǎo)致了我不好去封裝,最后不得已想到個(gè)折中辦法(下面會(huì)講)?;具@一輪下來我的興趣缺失了很多。
-
CoreData API介紹
- NSManagedObjectContext
這個(gè)對(duì)象有點(diǎn)像SQLite對(duì)象(個(gè)人理解:用來管理.xcdatamodeld中的數(shù)據(jù))。 負(fù)責(zé)數(shù)據(jù)和應(yīng)用庫之間的交互(CRUD,即增刪改查、保存等接口都是用這個(gè)對(duì)象調(diào)用). 每個(gè) NSManagedObjectContext 和其他 NSManagedObjectContext 都是完全獨(dú)立的。 所有的NSManagedObject(個(gè)人理解:實(shí)體數(shù)據(jù))都存在于NSManagedObjectContext中。 每個(gè)NSManagedObjectContext都知道自己管理著哪些NSManagedObject(個(gè)人理解:實(shí)體數(shù)據(jù)) // 創(chuàng)建方式(一般這個(gè)對(duì)象最好聲明成成員變量) self.context = [[NSManagedObjectContext alloc] init]; // 保存信息(比較重要的操作,每次更新和插入以及刪除后必須做的操作) NSError *error = nil; BOOL result = [self.context save:&error]; if (!result) { NSLog(@"添加數(shù)據(jù)失敗:%@",error); if (fail) { fail(error); } } else { NSLog(@"添加數(shù)據(jù)成功"); if (success) { success(); } }
- NSManagedObjectModel
Core Data的模型文件,有點(diǎn)像SQLite的.sqlite文件(個(gè)人理解:表示一個(gè).xcdatamodeld文件) // 創(chuàng)建方式 // 1.主動(dòng)加載指定名稱的.xcdatamodeld資源 //獲取模型路徑 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"]; //根據(jù)模型文件創(chuàng)建模型對(duì)象 NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; // 2.從應(yīng)用程序包中加載.xcdatamodeld模型文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
- NSPersistentStoreCoordinator
持久化存儲(chǔ)庫,CoreData的存儲(chǔ)類型(比如SQLite數(shù)據(jù)庫就是其中一種)。 用來將對(duì)象管理部分和持久化部分捆綁在一起,負(fù)責(zé)相互之間的交流(中介一樣) 用來設(shè)置CoreData存儲(chǔ)類型和存儲(chǔ)路徑 對(duì)象和數(shù)據(jù)庫之間的交互不需要我們來關(guān)心,蘋果幫我們做好了。我們只需要面向OC開發(fā) // 創(chuàng)建方式 /** 注意:創(chuàng)建NSPersistentStoreCoordinator前必須創(chuàng)建NSManagedObjectModel 個(gè)人理解:好比在一個(gè)數(shù)據(jù)庫中創(chuàng)建表前需要設(shè)置表的所有字段, 這里傳入NSManagedObjectModel模型(.xcdatamodeld模型文件), 感覺就是讓CoreData知道所有模型中的實(shí)體中的所有鍵值, 從而好創(chuàng)建CoreData數(shù)據(jù)庫 */ // 以傳入NSManagedObjectModel模型方式初始化持久化存儲(chǔ)庫 NSPersistentStoreCoordinator *persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; /* 持久化存儲(chǔ)庫的類型: NSSQLiteStoreType SQLite數(shù)據(jù)庫 NSBinaryStoreType 二進(jìn)制平面文件 NSInMemoryStoreType 內(nèi)存庫,無法永久保存數(shù)據(jù) 雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣 在幾乎所有的情景中,都應(yīng)該采用默認(rèn)設(shè)置,使用SQLite作為持久化存儲(chǔ)庫 */ // 添加一個(gè)持久化存儲(chǔ)庫并設(shè)置類型和路徑,NSSQLiteStoreType:SQLite作為存儲(chǔ)庫 NSError *error = nil; // 名字最好和.xcdatamodeld文件的名字一樣 NSString *sqlPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"xxx.sqlite"]; [persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlPath] options:nil error:&error]; if (error) { NSLog(@"添加數(shù)據(jù)庫失敗:%@",error); if (fail) { fail(error); } } else { NSLog(@"添加數(shù)據(jù)庫成功"); // 設(shè)置上下文所要關(guān)聯(lián)的持久化存儲(chǔ)庫(這一步千萬不要忘記) self.context.persistentStoreCoordinator = self.persistent; if (success) { success(); } }
- NSEntityDescription
用來描述實(shí)體(Entity):相當(dāng)于數(shù)據(jù)庫表中一組數(shù)據(jù)描述(純屬個(gè)人理解) 通過Core Data從數(shù)據(jù)庫中取出的對(duì)象,默認(rèn)情況下都是NSManagedObject對(duì)象. NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對(duì)象,通過鍵-值對(duì)來存取所有的實(shí)體屬性. setValue:forkey:存儲(chǔ)屬性值(屬性名為key); valueForKey:獲取屬性值(屬性名為key). 每個(gè)NSManagedObject都知道自己屬于哪個(gè)NSManagedObjectContext // 創(chuàng)建方式(用于插入數(shù)據(jù)使用:獲得實(shí)體,改變實(shí)體各個(gè)屬性值,保存后就代表插入) /** 注意:不能用 alloc init方式創(chuàng)建 通過傳入上下文和實(shí)體名稱,創(chuàng)建一個(gè)名稱對(duì)應(yīng)的實(shí)體對(duì)象 (相當(dāng)于數(shù)據(jù)庫一組數(shù)據(jù),其中含有多個(gè)字段) 個(gè)人感覺有種先插入一個(gè)新的Entity從而獲得Entity,在進(jìn)行各屬性賦值 */ NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName: entityName inManagedObjectContext:self.context];
- NSManagedObjectContext
-
CoreData的增刪改查
上面說了這么多始終沒描述如何去操作它,現(xiàn)在先說說大概步驟。
- insert
1.根據(jù)Entity名稱和NSManagedObjectContext獲取一個(gè)新的NSManagedObject NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.context]; 2.根據(jù)Entity中的鍵值,一一對(duì)應(yīng)通過setValue:forkey:給NSManagedObject對(duì)象賦值 [newEntity setValue:value forKey:key]; 3.保存修改 NSError *error = nil; BOOL result = [self.context save:&error];
- delete
1.通過查詢(設(shè)置好查詢條件)請(qǐng)求取得需要?jiǎng)h除的NSManagedObject的所有集合 2.通過for循環(huán)調(diào)用deleteObject:方法進(jìn)行逐個(gè)刪除(個(gè)人懷疑這個(gè)for循環(huán)會(huì)不會(huì)導(dǎo)致性能問題), 暫時(shí)沒發(fā)現(xiàn)其它刪除方法。 [self.context deleteObject:entity]; 3.保存修改
- update
1.通過查詢(設(shè)置好查詢條件)請(qǐng)求取得需要修改的NSManagedObject的所有集合 2.通過for循環(huán)調(diào)用NSManagedObject對(duì)象的setValue:forkey:方法給各個(gè)屬性賦值 3.保存修改
- read
1.創(chuàng)建NSFetchRequest查詢請(qǐng)求對(duì)象 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 2.設(shè)置需要查詢的實(shí)體描述NSEntityDescription NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.context]; request.entity = desc; 3.設(shè)置排序順序NSSortDescriptor對(duì)象集合(可選) request.sortDescriptors = descriptorArray; 4.設(shè)置條件過濾(可選) NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr]; request.predicate = predicate; 5.執(zhí)行查詢請(qǐng)求 NSError *error = nil; // NSManagedObject對(duì)象集合 NSArray *objs = [self.context executeFetchRequest:request error:&error]; // 查詢結(jié)果數(shù)目 NSUInteger count = [self.context countForFetchRequest:request error:&error];
從上面可以看出如果我們僅僅是簡單的對(duì)CoreData進(jìn)行增刪改查的操作的話,稍微費(fèi)點(diǎn)心思的就屬查詢這一塊。里面涉及到了兩個(gè)對(duì)象NSSortDescriptor和NSPredicate,分別用于設(shè)置排序和過濾查詢條件的,尤其是NSPredicate,它不僅僅用于CoreData,還被經(jīng)常用于數(shù)組的過濾。具體對(duì)這兩個(gè)對(duì)象的介紹我就不說了,感覺這一篇寫多了。下面我直接上代碼。不在多講了。
- insert
-
CoreData個(gè)人小封裝
上面提到實(shí)體的創(chuàng)建因素導(dǎo)致了封裝重用的難度,基本上新建一個(gè).xcdatamodeld文件就得做好特意為它寫一些操作接口的準(zhǔn)備。
- CoreDataAPI.h/.m
// // CoreDataAPI.h // TedcallStorage // // Created by tedcall on 16/7/1. // Copyright ? 2016年 pocket. All rights reserved. // #import <Foundation/Foundation.h> @interface CoreDataAPI : NSObject /** * 獲取數(shù)據(jù)庫存儲(chǔ)的路徑 */ @property (nonatomic,copy,readonly) NSString *sqlPath; /** * 獲取.xcdatamodeld文件的名稱 */ @property (nonatomic,copy,readonly) NSString *modelName; /** * 獲取.xcdatamodeld文件中創(chuàng)建的實(shí)體的名稱 */ @property (nonatomic,copy,readonly) NSString *entityName; /** * 創(chuàng)建CoreData數(shù)據(jù)庫 * * @param entityName 實(shí)體名稱 * @param modelName .xcdatamodeld文件名稱(為nil則主動(dòng)從程序包加載模型文件) * @param sqlPath 數(shù)據(jù)庫存儲(chǔ)的路徑 * @param success 成功回調(diào) * @param fail 失敗回調(diào) * * @return 返回CoreDataAPI對(duì)象 */ - (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 插入數(shù)據(jù) * * @param dict 字典中的鍵值對(duì)必須要與實(shí)體中的每個(gè)名字一一對(duì)應(yīng) * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 查詢數(shù)據(jù) * * @param sequenceKeys 數(shù)組高級(jí)排序(數(shù)組里存放實(shí)體中的key,順序按自己需要的先后存放即可),實(shí)體key來排序 * @param isAscending 是否上升排序 * @param filterStr 過濾語句 * @param success 成功后結(jié)果回調(diào) * @param fail 失敗回調(diào) */ - (void)readEntity:(NSArray *)sequenceKeys ascending:(BOOL)isAscending filterStr:(NSString *)filterStr success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail; /** * 刪除數(shù)據(jù) * * @param entity NSManagedObject * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)deleteEntity:(NSManagedObject *)entity success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 更新數(shù)據(jù) * * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail;
- CoreDataAPI.h/.m
@end
```
```objc
//
// CoreDataAPI.m
// TedcallStorage
//
// Created by tedcall on 16/7/1.
// Copyright ? 2016年 pocket. All rights reserved.
//
/**
* 知識(shí)點(diǎn):
NSManagedObject:
通過Core Data從數(shù)據(jù)庫中取出的對(duì)象,默認(rèn)情況下都是NSManagedObject對(duì)象.
NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對(duì)象,通過鍵-值對(duì)來存取所有的實(shí)體屬性.
setValue:forkey:存儲(chǔ)屬性值(屬性名為key);
valueForKey:獲取屬性值(屬性名為key).
每個(gè)NSManagedObject都知道自己屬于哪個(gè)NSManagedObjectContext
NSManagedObjectContext:
負(fù)責(zé)數(shù)據(jù)和應(yīng)用庫之間的交互(CRUD,即增刪改查、保存等接口都在這個(gè)對(duì)象中).
所有的NSManagedObject都存在于NSManagedObjectContext中,所以對(duì)象和context是相關(guān)聯(lián)的
每個(gè) context 和其他 context 都是完全獨(dú)立的
每個(gè)NSManagedObjectContext都知道自己管理著哪些NSManagedObject
NSPersistentStoreCoordinator:
添加持久化存儲(chǔ)庫,CoreData的存儲(chǔ)類型(比如SQLite數(shù)據(jù)庫就是其中一種)
中間審查者,用來將對(duì)象圖管理部分和持久化部分捆綁在一起,負(fù)責(zé)相互之間的交流(中介一樣)
NSManagedObjectModel:
Core Data的模型文件
NSEntityDescription:
用來描述實(shí)體:相當(dāng)于數(shù)據(jù)庫表中一組數(shù)據(jù)描述
*/
#import "CoreDataAPI.h"
#import <CoreData/CoreData.h>
@interface CoreDataAPI()
/**
* 數(shù)據(jù)模型對(duì)象
*/
@property (nonatomic,strong) NSManagedObjectModel *model;
/**
* 上下文
*/
@property (nonatomic,strong) NSManagedObjectContext *context;
/**
* 持久性存儲(chǔ)區(qū)
*/
@property (nonatomic,strong) NSPersistentStoreCoordinator *persistent;
@end
@implementation CoreDataAPI
- (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
if (self = [super init]) {
// 斷言(實(shí)體名稱和存儲(chǔ)路徑是否為nil)
// ...
_entityName = entityName;
_modelName = modelName;
_sqlPath = sqlPath;
// 初始化上下文
self.context = [[NSManagedObjectContext alloc] init];
if (modelName) {
//獲取模型路徑
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"];
//根據(jù)模型文件創(chuàng)建模型對(duì)象
self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
} else { // 從應(yīng)用程序包中加載模型文件
self.model = [NSManagedObjectModel mergedModelFromBundles:nil];
}
// 以傳入模型方式初始化持久化存儲(chǔ)庫
self.persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
/*
持久化存儲(chǔ)庫的類型:
NSSQLiteStoreType SQLite數(shù)據(jù)庫
NSBinaryStoreType 二進(jìn)制平面文件
NSInMemoryStoreType 內(nèi)存庫,無法永久保存數(shù)據(jù)
雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
在幾乎所有的情景中,都應(yīng)該采用默認(rèn)設(shè)置,使用SQLite作為持久化存儲(chǔ)庫
*/
// 添加一個(gè)持久化存儲(chǔ)庫并設(shè)置類型和路徑,NSSQLiteStoreType:SQLite作為存儲(chǔ)庫
NSError *error = nil;
[self.persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlPath] options:nil error:&error];
if (error) {
NSLog(@"添加數(shù)據(jù)庫失敗:%@",error);
if (fail) {
fail(error);
}
} else {
NSLog(@"添加數(shù)據(jù)庫成功");
// 設(shè)置上下文所要關(guān)聯(lián)的持久化存儲(chǔ)庫
self.context.persistentStoreCoordinator = self.persistent;
if (success) {
success();
}
}
}
return self;
}
// 添加數(shù)據(jù)
- (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
if (!dict||dict.allKeys.count == 0) return;
// 通過傳入上下文和實(shí)體名稱,創(chuàng)建一個(gè)名稱對(duì)應(yīng)的實(shí)體對(duì)象(相當(dāng)于數(shù)據(jù)庫一組數(shù)據(jù),其中含有多個(gè)字段)
NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:self.entityName inManagedObjectContext:self.context];
// 實(shí)體對(duì)象存儲(chǔ)屬性值(相當(dāng)于數(shù)據(jù)庫中將一個(gè)值存入對(duì)應(yīng)字段)
for (NSString *key in [dict allKeys]) {
[newEntity setValue:[dict objectForKey:key] forKey:key];
}
// 保存信息,同步數(shù)據(jù)
NSError *error = nil;
BOOL result = [self.context save:&error];
if (!result) {
NSLog(@"添加數(shù)據(jù)失敗:%@",error);
if (fail) {
fail(error);
}
} else {
NSLog(@"添加數(shù)據(jù)成功");
if (success) {
success();
}
}
}
// 查詢數(shù)據(jù)
- (void)readEntity:(NSArray *)sequenceKeys ascending:(BOOL)isAscending filterStr:(NSString *)filterStr success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail
{
// 1.初始化一個(gè)查詢請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// 2.設(shè)置要查詢的實(shí)體
NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.context];
request.entity = desc;
// 3.設(shè)置查詢結(jié)果排序
if (sequenceKeys&&sequenceKeys.count>0) { // 如果進(jìn)行了設(shè)置排序
NSMutableArray *array = [NSMutableArray array];
for (NSString *key in sequenceKeys) {
/**
* 設(shè)置查詢結(jié)果排序
* sequenceKey:根據(jù)某個(gè)屬性(相當(dāng)于數(shù)據(jù)庫某個(gè)字段)來排序
* isAscending:是否升序
*/
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:key ascending:isAscending];
[array addObject:sort];
}
if (array.count>0) {
request.sortDescriptors = array;// 可以添加多個(gè)排序描述器,然后按順序放進(jìn)數(shù)組即可
}
}
// 4.設(shè)置條件過濾
if (filterStr) { // 如果設(shè)置了過濾語句
NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr];
request.predicate = predicate;
}
// 5.執(zhí)行請(qǐng)求
NSError *error = nil;
NSArray *objs = [self.context executeFetchRequest:request error:&error]; // 獲得查詢數(shù)據(jù)數(shù)據(jù)集合
if (error) {
if (fail) {
fail(error);
}
} else{
if (success) {
success(objs);
}
}
}
// 更新數(shù)據(jù)
- (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
NSError *error = nil;
[self.context save:&error];
if (error) {
NSLog(@"刪除失?。?@",error);
if (fail) {
fail(error);
}
} else {
if (success) {
success();
}
}
}
// 刪除數(shù)據(jù)
- (void)deleteEntity:(NSManagedObject *)entity success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
// 傳入需要?jiǎng)h除的實(shí)體對(duì)象
[self.context deleteObject:entity];
// 同步到數(shù)據(jù)庫
NSError *error = nil;
[self.context save:&error];
if (error) {
NSLog(@"刪除失?。?@",error);
if (fail) {
fail(error);
}
} else {
if (success) {
success();
}
}
}
@end
```
- 用法(UploadCoreDataAPI.h/.m)
// // UploadCoreDataAPI.h // TedcallStorage // // Created by tedcall on 16/7/14. // Copyright ? 2016年 pocket. All rights reserved. // #import <Foundation/Foundation.h> @class ResourceModel,DownLoadModel; @interface UploadCoreDataAPI : NSObject /** * 上傳數(shù)據(jù)庫模型名稱 */ @property (nonatomic,copy,readonly) NSString *coreDataModelName; /** * 上傳數(shù)據(jù)庫實(shí)體名稱 */ @property (nonatomic,copy,readonly) NSString *coreDataEntityName; /** * 上傳數(shù)據(jù)庫存儲(chǔ)路徑 */ @property (nonatomic,copy,readonly) NSString *coreDataSqlPath; + (instancetype)sharedInstance; /** * 插入上傳記錄 * * @param model 數(shù)據(jù)模型 * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)insertUploadModel:(ResourceModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 更新上傳記錄 * * @param model 數(shù)據(jù)模型 * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)updateUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 刪除一條上傳記錄 * * @param model 數(shù)據(jù)模型 * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)deleteUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 刪除所有上傳記錄 * * @param success 成功回調(diào) * @param fail 失敗回調(diào) */ - (void)deleteAllUploadModel:(void(^)(void))success fail:(void(^)(NSError *error))fail; /** * 查詢上傳數(shù)據(jù)庫所有數(shù)據(jù) * * @param success 成功回調(diào)(finishArray:已完成(DownLoadModel對(duì)象數(shù)組) unfinishedArray:未完成(DownLoadModel對(duì)象數(shù)組)) * @param fail 失敗回調(diào) */ - (void)readAllUploadModel:(void(^)(NSArray *finishArray,NSArray *unfinishedArray))success fail:(void(^)(NSError *error))fail; @end
// // UploadCoreDataAPI.m // TedcallStorage // // Created by tedcall on 16/7/14. // Copyright ? 2016年 pocket. All rights reserved. // #import "UploadCoreDataAPI.h" #import "CoreDataAPI.h" #import "ResourceModel.h" #import "DownLoadModel.h" static NSString * const modelName = @"Upload"; static NSString * const entityName = @"UploadEntity"; static NSString * const sqliteName = @"Upload.sqlite"; @interface UploadCoreDataAPI() @property (nonatomic,strong) CoreDataAPI *uploadData; @end @implementation UploadCoreDataAPI static UploadCoreDataAPI *uploadCoreData = nil; + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ uploadCoreData = [[UploadCoreDataAPI alloc] init]; }); return uploadCoreData; } + (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (uploadCoreData == nil) { uploadCoreData = [super allocWithZone:zone]; } }); return uploadCoreData; } - (instancetype)init { if (self = [super init]) { [self initUploadCoreData]; } return self; } - (void)initUploadCoreData { _coreDataEntityName = entityName; _coreDataModelName = modelName; _coreDataSqlPath = [[[FileManager shardInstance] getDocumentPath] stringByAppendingPathComponent:sqliteName]; self.uploadData = [[CoreDataAPI alloc] initWithCoreData:self.coreDataEntityName modelName:self.coreDataModelName sqlPath:self.coreDataSqlPath success:^{ NSLog(@"initUploadCoreData success"); } fail:^(NSError *error) { NSLog(@"initUploadCoreData fail"); }]; } #pragma mark - -- 插入上傳記錄 - (void)insertUploadModel:(ResourceModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail { NSString *fileName = model.fileName; NSString *fileSize = [NSString stringWithFormat:@"%.2lf",model.size]; NSString *urlPath = model.path; NSNumber *time = [NSNumber numberWithInt:[[DateManager sharedInstance] getSecondsSince1970]]; NSNumber *fileType = [NSNumber numberWithInt:model.fileType]; NSNumber *finishStatus = [NSNumber numberWithBool:NO]; NSDictionary *dict = NSDictionaryOfVariableBindings(fileName,fileSize,urlPath,time,fileType,finishStatus); [self.uploadData insertNewEntity:dict success:^{ if (success) { success(); } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } #pragma mark - -- 更新上傳記錄 - (void)updateUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail { NSString *filterStr = [NSString stringWithFormat:@"time = %d AND urlPath = '%@' AND fileName = '%@'",model.time,model.urlPath,model.fileName]; [self.uploadData readEntity:nil ascending:YES filterStr:filterStr success:^(NSArray *results) { if (results.count>0) { NSManagedObject *obj = [results firstObject]; [obj setValue:[NSNumber numberWithBool:model.finishStatus] forKey:@"finishStatus"]; [self.uploadData updateEntity:^{ if (success) { success(); } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } #pragma mark - -- 刪除一條上傳記錄 - (void)deleteUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail { NSString *filterStr = [NSString stringWithFormat:@"time = %d AND urlPath = '%@' AND fileName = '%@'",model.time,model.urlPath,model.fileName]; [self.uploadData readEntity:nil ascending:YES filterStr:filterStr success:^(NSArray *results) { if (results.count>0) { NSManagedObject *obj = [results firstObject]; [self.uploadData deleteEntity:obj success:^{ if (success) { success(); } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } #pragma mark - -- 刪除所有上傳記錄 - (void)deleteAllUploadModel:(void(^)(void))success fail:(void(^)(NSError *error))fail { [self.uploadData readEntity:nil ascending:YES filterStr:nil success:^(NSArray *results) { for (NSManagedObject *obj in results){ [self.uploadData deleteEntity:obj success:^{ if (success) { success(); } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } #pragma mark - -- 查詢所有上傳記錄 - (void)readAllUploadModel:(void(^)(NSArray *finishArray,NSArray *unfinishedArray))success fail:(void(^)(NSError *error))fail { [self.uploadData readEntity:nil ascending:YES filterStr:nil success:^(NSArray *results) { NSMutableArray *finishArray = [NSMutableArray array]; NSMutableArray *unfinishedArray = [NSMutableArray array]; for (NSManagedObject *obj in results) { DownLoadModel *model = [[DownLoadModel alloc] init]; // 獲取數(shù)據(jù)庫中各個(gè)鍵值的值 model.fileName = [obj valueForKey:@"fileName"]; model.fileSize = [obj valueForKey:@"fileSize"]; model.urlPath = [obj valueForKey:@"urlPath"]; model.time = [[obj valueForKey:@"time"] intValue]; model.fileType = [[obj valueForKey:@"fileType"] intValue]; model.imageData = [obj valueForKey:@"imageData"]; model.finishStatus = [[obj valueForKey:@"finishStatus"] intValue]; if (model.finishStatus) { [finishArray addObject:model]; } else { [unfinishedArray addObject:model]; } } if (success) { success(finishArray,unfinishedArray); } } fail:^(NSError *error) { if (fail) { fail(error); } }]; } @end
結(jié)語:以上純屬個(gè)人摸索和個(gè)人總結(jié),不對(duì)的地方忘指出。其實(shí)在實(shí)際開發(fā)中不僅僅是增刪改查這么簡單,有時(shí)候會(huì)出現(xiàn)APP已經(jīng)發(fā)布,但是數(shù)據(jù)庫后續(xù)改變了,這就涉及到數(shù)據(jù)庫的遷移,以及一些數(shù)據(jù)安全問題和線程等都是比較深入的,有專研精神的可以自行搜索相關(guān)資料。互勉?。。?/p>