從設(shè)計(jì)一個(gè)自己的CoreData庫(kù)角度了解CoreData

主要參考objc.io關(guān)于CoreData的文章(https://www.objc.io/issues/4-core-data/ )和magicalRecord(https://github.com/magicalpanda/MagicalRecord )的設(shè)計(jì);ef

1.CoreData的結(jié)構(gòu):

要理解coreData,首先要了解它的結(jié)構(gòu),即它包含了哪些部分氛改、這些部分又是怎么關(guān)聯(lián)起來(lái)的游盲。先上個(gè)圖,從objc.io里盜來(lái)的圖:


CoreData架構(gòu)圖.png

coreData是一個(gè)數(shù)據(jù)庫(kù)管理框架炫贤,那么肯定要和數(shù)據(jù)庫(kù)打交道再登,圖里的File System就代表數(shù)據(jù)庫(kù)文件操作尔邓;誰(shuí)來(lái)直接管理這些,SQLite,可以理解coreData是建立在SQLite之上的锉矢,它內(nèi)部的數(shù)據(jù)操作仍然是SQLite.
SQLite本身是一個(gè)獨(dú)立的框架梯嗽,coreData想要使用它管理數(shù)據(jù)文件,肯定需要一個(gè)角色來(lái)負(fù)責(zé)沽损,這就是NSPersistentStore灯节。看一下NSPersistentStore的一般構(gòu)建方法,類似:

[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];

可以看得出绵估,對(duì)于它來(lái)說(shuō)炎疆,基本的需求是類型和地址,也就是決定在什么位置建立一個(gè)什么樣的數(shù)據(jù)庫(kù)国裳。所以圖的下半部分形入,或者說(shuō)coreData的整體功能的一端,就是和本地的數(shù)據(jù)庫(kù)文件交互缝左。

然后亿遂,從上面看。首先是NSManagedObject渺杉,它的類代表了一種數(shù)據(jù)模型蛇数,而類的每個(gè)對(duì)象對(duì)應(yīng)著一條數(shù)據(jù)。要寫的舒服開心少办,肯定是要有數(shù)據(jù)模型的苞慢,這樣存儲(chǔ)的過(guò)程就不需要程序員自己動(dòng)手了诵原。模型具有什么屬性英妓,是什么類型挽放,有這些,框架自身就能處理好數(shù)據(jù)以什么格式寫入的問(wèn)題蔓纠,所以也就可以擺脫SQL語(yǔ)句拉辑畦,我們也就可以像使用其他類一樣處理NSManagedObject了。

而NSManagedObjectContext腿倚,一般命名里帶Context的纯出,都像是一個(gè)工作場(chǎng)地,它們把一個(gè)操作的所需要的東西都保存進(jìn)來(lái)敷燎,統(tǒng)一管理配置暂筝,就像繪圖的CGContextRef。這里的NSManagedObjectContext就是管理NSManagedObject的硬贯,設(shè)想焕襟,你在內(nèi)存里構(gòu)建了一個(gè)對(duì)象,也就是一條數(shù)據(jù)饭豹,你修改它了鸵赖,誰(shuí)知道?刪除它了拄衰,誰(shuí)知道它褪?都是NSManagedObjectContext在監(jiān)控,只有把所有的增刪改查都保存下來(lái)了翘悉,那么到時(shí)只要save一下茫打,內(nèi)存里改變的東西才會(huì)如實(shí)的反饋到數(shù)據(jù)庫(kù)文件里。

最后镐确,回到中間包吝,NSPersistentStoreCoordinator;很明顯源葫,它是連接兩端的紐帶诗越。所以整個(gè)流程就是:1、數(shù)據(jù)(NSManagedObject)被創(chuàng)建修改或刪除等等息堂,這些都被context看在眼里嚷狞,然后你要保存了,context把修改的信息提交給荣堰,context根據(jù)數(shù)據(jù)信息找到正確的數(shù)據(jù)庫(kù)文件床未,根據(jù)數(shù)據(jù)模型,正確的把數(shù)據(jù)寫入到數(shù)據(jù)庫(kù)文件里振坚。2薇搁、查詢時(shí),context把查詢條件提交給Coordinator渡八,它去數(shù)據(jù)庫(kù)文件里把數(shù)據(jù)查出來(lái)啃洋,給context,context再把這些數(shù)據(jù)和以在它管理內(nèi)的結(jié)合传货。

2.構(gòu)建一個(gè)CoreData stack:

上面圖里的整個(gè)聯(lián)系在一起的東西稱為一個(gè)CoreData stack,那么怎么用代碼把真?zhèn)€框架搭起來(lái)呢宏娄?

構(gòu)建的入口在模型问裕,模型決定了數(shù)據(jù)庫(kù)表該建成啥樣,context里的數(shù)據(jù)該是什么樣孵坚。因?yàn)槭菑脑O(shè)計(jì)模塊的角度來(lái)認(rèn)識(shí)CoreData,所以還要考慮下類設(shè)計(jì)的問(wèn)題:通過(guò)模型文件構(gòu)建代碼NSPersistentStoreCoordinator粮宛,由它再構(gòu)建其它的,所以完全可以使用一個(gè)類方法卖宠,只提供一個(gè)字符串做參數(shù)巍杈,構(gòu)建整個(gè)CoreData stack,那么以后使用CoreData的時(shí)候扛伍,一句話搞定秉氧。

所以我定義了一個(gè)DataManager的類,提供一個(gè)類方法:

+(BOOL)setupDataBaseWithModelName:(NSString*)modelName;
+(BOOL)setupDataBaseWithModelName:(NSString *)modelName{
    if ([DataManager shareInstance].coordinator) {
        return [DataManager shareInstance].coordinator;
    }
    //(1)構(gòu)建Coordinator
    NSString* filePath = [[NSBundle mainBundle]pathForResource:modelName ofType:@"momd"];
    NSURL* modelURL = [NSURL URLWithString:filePath];
    NSAssert(modelURL, @"數(shù)據(jù)模型文件缺失");
    
    NSManagedObjectModel* model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    NSPersistentStoreCoordinator* coordinator = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];
    
    //(2)構(gòu)建數(shù)據(jù)庫(kù)文件
    NSError* error = nil;
    NSURL* storeURL = [NSPersistentStore storeURLForModelName:modelName];
    
    [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
    if (error) {
        NSLog(@"%@",[error localizedDescription]);
        return NO;
    }
    [DataManager shareInstance].coordinator = coordinator;
    
    //(3)構(gòu)建context
    [NSManagedObjectContext setDefaultContextWithCoordinator:coordinator];
    
    return YES;
}

(案例代碼蜒秤,放在了https://github.com/FindCrt/coreDataExample_sinaWeibo汁咏, CoreData相關(guān)的代碼都在coreDataModel文件夾里 )
注釋標(biāo)記了3個(gè)部分,第一部分構(gòu)建Coordinator作媚,有文件構(gòu)建模型NSManagedObjectModel攘滩,由模型構(gòu)建Coordinator;NSManagedObjectModel還有一個(gè)比較有意思的方法:

+ (nullable NSManagedObjectModel *)mergedModelFromBundles:(nullable NSArray<NSBundle *> *)bundles;

是把指定bundle里面的模型文件合成了一個(gè)模型纸泡,統(tǒng)一管理了漂问,這樣也好。否則得多個(gè)Coordinator女揭,那查某個(gè)數(shù)據(jù)蚤假,還得分清是哪個(gè)Coordinator。

第二部分由Coordinator構(gòu)建數(shù)據(jù)庫(kù)吧兔,地址我是用一個(gè)NSPersistentStore的category方法來(lái)提供的磷仰,把文件存在沙盒的application support文件夾下,使用模型名區(qū)別境蔼。在magicalRecord里灶平,大量的使用了category,好處是分工明確。比如一個(gè)數(shù)據(jù)庫(kù)文件的地址箍土,這個(gè)肯定是和NSPersistentStore一一對(duì)應(yīng)的逢享,也是屬于它使用的東西,使用category吴藻,把這個(gè)任務(wù)交給它自身瞒爬,多好。

第三部分,是構(gòu)建context侧但。為了在多線程的情況下能夠使用Coredata吆你,構(gòu)建了一系列相關(guān)聯(lián)的context。這個(gè)等下專門寫俊犯。

基本的基礎(chǔ)建設(shè)好,就要提供服務(wù)了:增刪改查的操作伤哺。

3.增刪改查服務(wù):
//添加
+(NSManagedObject*)createEntityNamedWith:(NSString*)entityName;

+(NSManagedObject *)createEntityNamedWith:(NSString *)entityName{
    NSManagedObject* entity = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];
    
    return entity;
}

提供了兩個(gè)參數(shù)燕侠,一個(gè)實(shí)體名、一個(gè)context,數(shù)據(jù)都要建在context來(lái)管理立莉,肯定要指定context绢彤;然后context是關(guān)聯(lián)到Coordinator的,而Coordinator有所有的模型數(shù)據(jù)蜓耻,那么通過(guò)名字就可以知道這個(gè)實(shí)體的數(shù)據(jù)結(jié)構(gòu)了茫舶,這樣就沒(méi)有問(wèn)題了。

//刪除
+(void)deleteEntity:(NSManagedObject*)entity;

+(void)deleteEntity:(NSManagedObject *)entity{
    [entity.managedObjectContext deleteObject:entity];
}

很簡(jiǎn)單刹淌,context有提供刪除方法饶氏,只要實(shí)體的context把它自身刪除即可。這個(gè)執(zhí)行過(guò)程暴露了一個(gè)問(wèn)題有勾,就是數(shù)據(jù)只由context來(lái)管疹启,如果一個(gè)數(shù)據(jù)的context沒(méi)了,它就沒(méi)法做任何操作了蔼卡。

//修改
+(BOOL)saveContextForCurrentThead;

+(BOOL)saveContextForCurrentThead{
    return [[NSManagedObjectContext contextForCurrentThread]save];
}

對(duì)數(shù)據(jù)對(duì)象的修改喊崖,都會(huì)被記錄,所以修改的關(guān)鍵不是修改雇逞,而是如何把內(nèi)存里數(shù)據(jù)的修改同步到數(shù)據(jù)庫(kù)文件里荤懂,也就是保存的問(wèn)題。context本身提供了修改塘砸,考慮到多線程處理节仿,自定義了save來(lái)做了一些處理。

//查詢
+(NSArray*)fetchEntity:(NSString*)entityName withPredicate:(NSPredicate*)predicate;

+(NSArray *)fetchEntity:(NSString *)entityName withPredicate:(NSPredicate *)predicate{
    NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:entityName];
    request.predicate = predicate;
    
    NSError* error = nil;
    NSArray* result = [[NSManagedObjectContext contextForCurrentThread]executeFetchRequest:request error:&error];
    if (!result) {
        NSLog(@"fetch %@ error :%@",entityName,[error localizedDescription]);
        return nil;
    }
    return result;
}

因?yàn)閿?shù)據(jù)對(duì)象都是context在管理掉蔬,所以查詢也是它來(lái)處理粟耻。CoreData專門用于查詢的類NSFetchRequest,它有許多的屬性眉踱,可以滿足豐富的查詢需求挤忙,但最基本的兩個(gè)是: entityName和predicate。也就是指定查哪個(gè)數(shù)據(jù)谈喳,使用什么篩選條件册烈。

關(guān)于查詢,有一個(gè)概念是fault和fire fault。當(dāng)你查詢的時(shí)候赏僧,數(shù)據(jù)并沒(méi)有加載到內(nèi)存里大猛,得到的只是每條數(shù)據(jù)的ID,在手機(jī)里的地址等淀零,這時(shí)數(shù)據(jù)其實(shí)是空的挽绩,稱為fault;當(dāng)你訪問(wèn)了這個(gè)數(shù)據(jù)對(duì)象的一個(gè)屬性的時(shí)候驾中,才會(huì)把實(shí)際數(shù)據(jù)加載到內(nèi)存里唉堪,也就是真的用到的時(shí)候才加載。這種機(jī)制也是經(jīng)常出現(xiàn)的吧肩民。

從這里可以看出唠亚,我們需要處理的是上面架構(gòu)圖里的上部分,而CoreData幫我們把處理反饋到下部分的數(shù)據(jù)庫(kù)文件里持痰。所以操作的方向是自上而下的毒费,然后看了下各個(gè)類的引用觉增,果然是:
NSManagedObject有屬性NSManagedObjectContext *managedObjectContext,
而NSManagedObjectContext有屬性NSPersistentStoreCoordinator *persistentStoreCoordinator;
而NSPersistentStoreCoordinator有NSArray<__kindof NSPersistentStore *> *persistentStores;

4.多線程下CoreData處理:

寫在下一篇了:http://www.reibang.com/p/29d92e3d0e70

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市侧甫,隨后出現(xiàn)的幾起案子凶伙,更是在濱河造成了極大的恐慌号杏,老刑警劉巖瓤摧,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異纵苛,居然都是意外死亡剿涮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門攻人,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)取试,“玉大人,你說(shuō)我怎么就攤上這事怀吻∷才ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蓬坡,是天一觀的道長(zhǎng)猿棉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)屑咳,這世上最難降的妖魔是什么萨赁? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮兆龙,結(jié)果婚禮上杖爽,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好慰安,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布腋寨。 她就那樣靜靜地躺著,像睡著了一般化焕。 火紅的嫁衣襯著肌膚如雪萄窜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天撒桨,我揣著相機(jī)與錄音查刻,去河邊找鬼。 笑死元莫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蝶押。 我是一名探鬼主播踱蠢,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼棋电!你這毒婦竟也來(lái)了茎截?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赶盔,失蹤者是張志新(化名)和其女友劉穎企锌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體于未,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撕攒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烘浦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抖坪。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖闷叉,靈堂內(nèi)的尸體忽然破棺而出擦俐,到底是詐尸還是另有隱情,我是刑警寧澤握侧,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布蚯瞧,位于F島的核電站,受9級(jí)特大地震影響品擎,放射性物質(zhì)發(fā)生泄漏埋合。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一萄传、第九天 我趴在偏房一處隱蔽的房頂上張望饥悴。 院中可真熱鬧,春花似錦、人聲如沸西设。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贷揽。三九已至棠笑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間禽绪,已是汗流浹背蓖救。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留印屁,地道東北人循捺。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像雄人,于是被迫代替她去往敵國(guó)和親从橘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容