該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請注明:劉小壯
CoreData
使用相關(guān)的技術(shù)點已經(jīng)講差不多了蹄皱,我所掌握的也就這么多了....在本篇文章中主要講
CoreData
的多線程所森,其中會包括并發(fā)隊列類型囱持、線程安全等技術(shù)點。我對多線程的理解可能不是太透徹焕济,文章中出現(xiàn)的問題還請各位指出纷妆。在之后公司項目使用CoreData
的過程中,我會將其中遇到的多線程相關(guān)的問題更新到文章中晴弃。在文章的最后掩幢,會根據(jù)我對
CoreData
多線程的學(xué)習(xí),以及在工作中的具體使用上鞠,給出一些關(guān)于多線程結(jié)構(gòu)的設(shè)計建議际邻,各位可以當(dāng)做參考。文章中如有疏漏或錯誤芍阎,還請各位及時提出世曾,謝謝!??
MOC并發(fā)隊列類型
在CoreData
中MOC
是支持多線程的谴咸,可以在創(chuàng)建MOC
對象時轮听,指定其并發(fā)隊列的類型。當(dāng)指定隊列類型后岭佳,系統(tǒng)會將操作都放在指定的隊列中執(zhí)行血巍,如果指定的是私有隊列,系統(tǒng)會創(chuàng)建一個新的隊列珊随。但這都是系統(tǒng)內(nèi)部的行為述寡,我們并不能獲取這個隊列,隊列由系統(tǒng)所擁有叶洞,并由系統(tǒng)將任務(wù)派發(fā)到這個隊列中執(zhí)行的鲫凶。
NSManagedObjectContext并發(fā)隊列類型:
NSConfinementConcurrencyType : 如果使用
init
方法初始化上下文,默認就是這個并發(fā)類型衩辟。這個枚舉值是不支持多線程的掀序,從名字上也體現(xiàn)出來了。NSPrivateQueueConcurrencyType : 私有并發(fā)隊列類型惭婿,操作都是在子線程中完成的。
NSMainQueueConcurrencyType : 主并發(fā)隊列類型叶雹,如果涉及到
UI
相關(guān)的操作财饥,應(yīng)該考慮使用這個枚舉值初始化上下文。
其中NSConfinementConcurrencyType
類型在iOS9
之后已經(jīng)被蘋果廢棄折晦,不建議使用這個API
钥星。使用此類型創(chuàng)建的MOC
挤巡,調(diào)用某些比較新的CoreData
的API
可能會導(dǎo)致崩潰钟病。
MOC多線程調(diào)用方式
在CoreData
中MOC不是線程安全的授翻,在多線程情況下使用MOC
時去扣,不能簡單的將MOC
從一個線程中傳遞到另一個線程中使用,這并不是CoreData
的多線程宁改,而且會出問題缕探。對于MOC
多線程的使用,蘋果給出了自己的解決方案还蹲。
在創(chuàng)建的MOC
中使用多線程爹耗,無論是私有隊列還是主隊列,都應(yīng)該采用下面兩種多線程的使用方式谜喊,而不是自己手動創(chuàng)建線程潭兽。調(diào)用下面方法后,系統(tǒng)內(nèi)部會將任務(wù)派發(fā)到不同的隊列中執(zhí)行斗遏∩截裕可以在不同的線程中調(diào)用MOC
的這兩個方法,這個是允許的诵次。
- (void)performBlock:(void (^)())block 異步執(zhí)行的block账蓉,調(diào)用之后會立刻返回。
- (void)performBlockAndWait:(void (^)())block 同步執(zhí)行的block藻懒,調(diào)用之后會等待這個任務(wù)完成剔猿,才會繼續(xù)向下執(zhí)行。
下面是多線程調(diào)用的示例代碼嬉荆,在多線程的環(huán)境下執(zhí)行MOC
的save
方法归敬,就是將save
方法放在MOC
的block
體中異步執(zhí)行,其他方法的調(diào)用也是一樣的鄙早。
[context performBlock:^{
[context save:nil];
}];
但是需要注意的是汪茧,這兩個block
方法不能在NSConfinementConcurrencyType
類型的MOC
下調(diào)用,這個類型的MOC
是不支持多線程的限番,只支持其他兩種并發(fā)方式的MOC
舱污。
多線程的使用
在業(yè)務(wù)比較復(fù)雜的情況下,需要進行大量數(shù)據(jù)處理弥虐,并且還需要涉及到UI
的操作扩灯。對于這種復(fù)雜需求,如果都放在主隊列中霜瘪,對性能和界面流暢度都會有很大的影響珠插,導(dǎo)致用戶體驗非常差,降低屏幕FPS
颖对。對于這種情況捻撑,可以采取多個MOC
配合的方式。
CoreData
多線程的發(fā)展中,在iOS5
經(jīng)歷了一次比較大的變化顾患,之后可以更方便的使用多線程番捂。從iOS5
開始,支持設(shè)置MOC
的parentContext
屬性江解,通過這個屬性可以設(shè)置MOC
的父MOC
设预。下面會針對iOS5
之前和之后,分別講解CoreData
的多線程使用膘流。
盡管現(xiàn)在的開發(fā)中早就不兼容iOS5
之前的系統(tǒng)了絮缅,但是作為了解這里還是要講一下,而且這種同步方式在iOS5
之后也是可以正常使用的呼股,也有很多人還在使用這種同步方式耕魄,下面其他章節(jié)也是同理。
iOS5之前使用多個MOC
在iOS5
之前實現(xiàn)MOC
的多線程彭谁,可以創(chuàng)建多個MOC
吸奴,多個MOC
使用同一個PSC
,并讓多個MOC
實現(xiàn)數(shù)據(jù)同步缠局。通過這種方式不用擔(dān)心PSC
在調(diào)用過程中的線程問題则奥,MOC
在使用PSC
進行save
操作時,會對PSC
進行加鎖狭园,等當(dāng)前加鎖的MOC
執(zhí)行完操作之后读处,其他MOC
才能繼續(xù)執(zhí)行操作。
每一個PSC
都對應(yīng)著一個持久化存儲區(qū)唱矛,PSC
知道存儲區(qū)中數(shù)據(jù)存儲的數(shù)據(jù)結(jié)構(gòu)罚舱,而MOC
需要使用這個PSC
進行save
操作的實現(xiàn)。
這樣做有一個問題绎谦,當(dāng)一個MOC
發(fā)生改變并持久化到本地時管闷,系統(tǒng)并不會將其他MOC
緩存在內(nèi)存中的NSManagedObject
對象改變。所以這就需要我們在MOC
發(fā)生改變時窃肠,將其他MOC
數(shù)據(jù)更新包个。
根據(jù)上面的解釋,在下面例子中創(chuàng)建了一個主隊列的mainMOC
冤留,主要用于UI
操作碧囊。一個私有隊列的backgroundMOC
,用于除UI
之外的耗時操作纤怒,兩個MOC
使用的同一個PSC
糯而。
// 獲取PSC實例對象
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// 創(chuàng)建托管對象模型,并指明加載Company模型文件
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"Company" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
// 創(chuàng)建PSC對象肪跋,并將托管對象模型當(dāng)做參數(shù)傳入,其他MOC都是用這一個PSC土砂。
NSPersistentStoreCoordinator *PSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 根據(jù)指定的路徑州既,創(chuàng)建并關(guān)聯(lián)本地數(shù)據(jù)庫
NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite", @"Company"];
[PSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
return PSC;
}
// 初始化用于本地存儲的所有MOC
- (void)createManagedObjectContext {
// 創(chuàng)建PSC實例對象谜洽,其他MOC都用這一個PSC。
NSPersistentStoreCoordinator *PSC = self.persistentStoreCoordinator;
// 創(chuàng)建主隊列MOC吴叶,用于執(zhí)行UI操作
NSManagedObjectContext *mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainMOC.persistentStoreCoordinator = PSC;
// 創(chuàng)建私有隊列MOC阐虚,用于執(zhí)行其他耗時操作
NSManagedObjectContext *backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundMOC.persistentStoreCoordinator = PSC;
// 通過監(jiān)聽NSManagedObjectContextDidSaveNotification通知,來獲取所有MOC的改變消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
// MOC改變后的通知回調(diào)
- (void)contextChanged:(NSNotification *)noti {
NSManagedObjectContext *MOC = noti.object;
// 這里需要做判斷操作蚌卤,判斷當(dāng)前改變的MOC是否我們將要做同步的MOC实束,如果就是當(dāng)前MOC自己做的改變,那就不需要再同步自己了逊彭。
// 由于項目中可能存在多個PSC咸灿,所以下面還需要判斷PSC是否當(dāng)前操作的PSC,如果不是當(dāng)前PSC則不需要同步侮叮,不要去同步其他本地存儲的數(shù)據(jù)避矢。
[MOC performBlock:^{
// 直接調(diào)用系統(tǒng)提供的同步API,系統(tǒng)內(nèi)部會完成同步的實現(xiàn)細節(jié)囊榜。
[MOC mergeChangesFromContextDidSaveNotification:noti];
}];
}
在上面的Demo
中审胸,創(chuàng)建了一個PSC
,并將其他MOC
都關(guān)聯(lián)到這個PSC
上卸勺,這樣所有的MOC
執(zhí)行本地持久化相關(guān)的操作時砂沛,都是通過同一個PSC
進行操作的。并在下面添加了一個通知曙求,這個通知是監(jiān)聽所有MOC
執(zhí)行save
操作后的通知碍庵,并在通知的回調(diào)方法中進行數(shù)據(jù)的合并。
iOS5之后使用多個MOC
在iOS5
之后圆到,MOC
可以設(shè)置parentContext
怎抛,一個parentContext
可以擁有多個ChildContext
。在ChildContext
執(zhí)行save
操作后芽淡,會將操作push
到parentContext
马绝,由parentContext
去完成真正的save
操作,而ChildContext
所有的改變都會被parentContext
所知曉挣菲,這解決了之前MOC
手動同步數(shù)據(jù)的問題富稻。
需要注意的是,在ChildContext
調(diào)用save
方法之后白胀,此時并沒有將數(shù)據(jù)寫入存儲區(qū)椭赋,還需要調(diào)用parentContext
的save
方法。因為ChildContext
并不擁有PSC
或杠,ChildContext
也不需要設(shè)置PSC
哪怔,所以需要parentContext
調(diào)用PSC
來執(zhí)行真正的save
操作。也就是只有擁有PSC
的MOC
執(zhí)行save
操作后,才是真正的執(zhí)行了寫入存儲區(qū)的操作认境。
- (void)createManagedObjectContext {
// 創(chuàng)建PSC實例對象胚委,還是用上面Demo的實例化代碼
NSPersistentStoreCoordinator *PSC = self.persistentStoreCoordinator;
// 創(chuàng)建主隊列MOC,用于執(zhí)行UI操作
NSManagedObjectContext *mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainMOC.persistentStoreCoordinator = PSC;
// 創(chuàng)建私有隊列MOC叉信,用于執(zhí)行其他耗時操作亩冬,backgroundMOC并不需要設(shè)置PSC
NSManagedObjectContext *backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundMOC.parentContext = mainMOC;
// 私有隊列的MOC和主隊列的MOC,在執(zhí)行save操作時硼身,都應(yīng)該調(diào)用performBlock:方法硅急,在自己的隊列中執(zhí)行save操作。
// 私有隊列的MOC執(zhí)行完自己的save操作后佳遂,還調(diào)用了主隊列MOC的save方法营袜,來完成真正的持久化操作,否則不能持久化到本地
[backgroundMOC performBlock:^{
[backgroundMOC save:nil];
[mainMOC performBlock:^{
[mainMOC save:nil];
}];
}];
}
上面例子中創(chuàng)建一個主隊列的mainMOC
讶迁,來完成UI
相關(guān)的操作连茧。創(chuàng)建私有隊列的backgroundMOC
,處理復(fù)雜邏輯以及數(shù)據(jù)處理操作巍糯,在實際開發(fā)中可以根據(jù)需求創(chuàng)建多個backgroundMOC
啸驯。需要注意的是,在backgroundMOC
執(zhí)行完save
方法后祟峦,又在mainMOC
中執(zhí)行了一次save
方法罚斗,這步是很重要的。
iOS5之前進行數(shù)據(jù)同步
就像上面章節(jié)中講到的宅楞,在iOS5
之前存在多個MOC
的情況下针姿,一個MOC
發(fā)生更改并提交存儲區(qū)后,其他MOC
并不知道這個改變距淫,其他MOC
和本地存儲的數(shù)據(jù)是不同步的,所以就涉及到數(shù)據(jù)同步的問題婶希。
進行數(shù)據(jù)同步時缴啡,會遇到多種復(fù)雜情況碘裕。例如只有一個MOC
數(shù)據(jù)發(fā)生了改變,其他MOC
更新時并沒有對相同的數(shù)據(jù)做改變揩页,這樣不會造成沖突旷偿,可以直接將其他MOC
更新。
如果在一個MOC
數(shù)據(jù)發(fā)生改變后爆侣,其他MOC
對相同的數(shù)據(jù)做了改變萍程,而且改變的結(jié)果不同,這樣在同步時就會造成沖突兔仰。下面將會按照這兩種情況茫负,分別講一下不同情況下的沖突處理方式。
簡單情況下的數(shù)據(jù)同步
簡單情況下的數(shù)據(jù)同步乎赴,是針對于只有一個MOC
的數(shù)據(jù)發(fā)生改變忍法,并提交存儲區(qū)后,其他MOC
更新時并沒有對相同的數(shù)據(jù)做改變榕吼,只是單純的同步數(shù)據(jù)的情況饿序。
在NSManagedObjectContext
類中,根據(jù)不同操作定義了一些通知羹蚣。在一個MOC
發(fā)生改變時原探,其他地方可以通過MOC
中定義的通知名,來獲取MOC
發(fā)生的改變度宦。在NSManagedObjectContext
中定義了下面三個通知:
NSManagedObjectContextWillSaveNotification
MOC
將要向存儲區(qū)存儲數(shù)據(jù)時踢匣,調(diào)用這個通知。在這個通知中不能獲取發(fā)生改變相關(guān)的NSManagedObject
對象戈抄。NSManagedObjectContextDidSaveNotification
MOC
向存儲區(qū)存儲數(shù)據(jù)后离唬,調(diào)用這個通知。在這個通知中可以獲取改變划鸽、添加输莺、刪除等信息戚哎,以及相關(guān)聯(lián)的NSManagedObject
對象。NSManagedObjectContextObjectsDidChangeNotification 在
MOC
中任何一個托管對象發(fā)生改變時嫂用,調(diào)用這個通知型凳。例如修改托管對象的屬性。
通過監(jiān)聽NSManagedObjectContextDidSaveNotification
通知嘱函,獲取所有MOC
的save
操作甘畅。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(settingsContext:) name:NSManagedObjectContextDidSaveNotification object:nil];
不需要在通知的回調(diào)方法中,編寫代碼對比被修改的托管對象往弓。MOC
為我們提供了下面的方法疏唾,只需要將通知對象傳入,系統(tǒng)會自動同步數(shù)據(jù)函似。
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification;
下面是通知中的實現(xiàn)代碼槐脏,但是需要注意的是,由于通知是同步執(zhí)行的撇寞,在通知對應(yīng)的回調(diào)方法中所處的線程顿天,和發(fā)出通知的MOC
執(zhí)行操作時所處的線程是同一個線程,也就是系統(tǒng)performBlock:
回調(diào)方法分配的線程蔑担。
所以其他MOC
在通知回調(diào)方法中牌废,需要注意使用performBlock:
方法,并在block
體中執(zhí)行操作啤握。
- (void)settingsContext:(NSNotification *)noti {
[context performBlock:^{
// 調(diào)用需要同步的MOC對象的merge方法畔规,直接將通知對象當(dāng)做參數(shù)傳進去即可,系統(tǒng)會完成同步操作恨统。
[context mergeChangesFromContextDidSaveNotification:noti];
}];
}
復(fù)雜情況下的數(shù)據(jù)同步
在一個MOC
對本地存儲區(qū)的數(shù)據(jù)發(fā)生改變叁扫,而其他MOC
也對同樣的數(shù)據(jù)做了改變,這樣后面執(zhí)行save
操作的MOC
就會沖突畜埋,并導(dǎo)致后面的save
操作失敗莫绣,這就是復(fù)雜情況下的數(shù)據(jù)合并。
這是因為每次一個MOC
執(zhí)行一次fetch
操作后悠鞍,會保存一個本地持久化存儲的狀態(tài)对室,當(dāng)下次執(zhí)行save
操作時會對比這個狀態(tài)和本地持久化狀態(tài)是否一樣。如果一樣咖祭,則代表本地沒有其他MOC
對存儲發(fā)生過改變掩宜;如果不一樣,則代表本地持久化存儲被其他MOC
改變過么翰,這就是造成沖突的根本原因牺汤。
對于這種沖突的情況,可以通過MOC
對象指定解決沖突的方案浩嫌,通過mergePolicy
屬性來設(shè)置方案檐迟。mergePolicy
屬性有下面幾種可選的策略补胚,默認是NSErrorMergePolicy
方式,這也是唯一一個有NSError
返回值的選項追迟。
NSErrorMergePolicy : 默認值溶其,當(dāng)出現(xiàn)合并沖突時,返回一個
NSError
對象來描述錯誤敦间,而MOC
和持久化存儲區(qū)不發(fā)生改變瓶逃。NSMergeByPropertyStoreTrumpMergePolicy : 以本地存儲為準,使用本地存儲來覆蓋沖突部分廓块。
NSMergeByPropertyObjectTrumpMergePolicy : 以
MOC
的為準金闽,使用MOC
來覆蓋本地存儲的沖突部分。NSOverwriteMergePolicy : 以
MOC
為準剿骨,用MOC
的所有NSManagedObject
對象覆蓋本地存儲的對應(yīng)對象。NSRollbackMergePolicy : 以本地存儲為準埠褪,
MOC
所有的NSManagedObject
對象被本地存儲的對應(yīng)對象所覆蓋浓利。
上面五種策略中,除了第一個NSErrorMergePolicy
的策略钞速,其他四種中NSMergeByPropertyStoreTrumpMergePolicy
和NSRollbackMergePolicy
贷掖,以及NSMergeByPropertyObjectTrumpMergePolicy
和NSOverwriteMergePolicy
看起來是重復(fù)的。
其實它們并不是沖突的渴语,這四種策略的不同體現(xiàn)在苹威,對沒有發(fā)生沖突的部分應(yīng)該怎么處理。NSMergeByPropertyStoreTrumpMergePolicy
和NSMergeByPropertyObjectTrumpMergePolicy
對沒有沖突的部分驾凶,未沖突部分數(shù)據(jù)并不會受到影響牙甫。而NSRollbackMergePolicy
和NSOverwriteMergePolicy
則是無論是否沖突,直接全部替換调违。
題外話:
對于MOC
的這種合并策略來看窟哺,有木有感覺到CoreData
解決沖突的方式,和SVN
解決沖突的方式特別像技肩。且轨。。
線程安全
無論是MOC
還是托管對象虚婿,都不應(yīng)該在其他MOC
的線程中執(zhí)行操作旋奢,這兩個API都不是線程安全的。但MOC
可以在其他MOC
線程中調(diào)用performBlock:
方法然痊,切換到自己的線程執(zhí)行操作至朗。
如果其他MOC
想要拿到托管對象,并在自己的隊列中使用托管對象剧浸,這是不允許的爽丹,托管對象是不能直接傳遞到其他MOC
的線程的筑煮。但是可以通過獲取NSManagedObject
的NSManagedObjectID
對象,在其他MOC
中通過NSManagedObjectID
對象粤蝎,從持久化存儲區(qū)中獲取NSManagedObject
對象真仲,這樣就是允許的。NSManagedObjectID
是線程安全初澎,并且可以跨線程使用的秸应。
可以通過MOC
獲取NSManagedObjectID
對應(yīng)的NSManagedObject
對象,例如下面幾個MOC
的API
碑宴。
NSManagedObject *object = [context objectRegisteredForID:objectID];
NSManagedObject *object = [context objectWithID:objectID];
通過NSManagedObject
對象的objectID
屬性软啼,獲取NSManagedObjectID
類型的objectID
對象。
NSManagedObjectID *objectID = object.objectID;
CoreData多線程結(jié)構(gòu)設(shè)計
上面章節(jié)中寫的大多都是怎么用CoreData
多線程延柠,在掌握多線程的使用后祸挪,就可以根據(jù)公司業(yè)務(wù)需求,設(shè)計一套CoreData
多線程結(jié)構(gòu)了贞间。對于多線程結(jié)構(gòu)的設(shè)計贿条,應(yīng)該本著盡量減少主線程壓力的角度去設(shè)計,將所有耗時操作都放在子線程中執(zhí)行增热。
對于具體的設(shè)計我根據(jù)不同的業(yè)務(wù)需求整以,給出兩種設(shè)計方案的建議。
兩層設(shè)計方案
在項目中多線程操作比較簡單時峻仇,可以創(chuàng)建一個主隊列mainMOC
公黑,和一個或多個私有隊列的backgroundMOC
。將所有backgroundMOC
的parentContext
設(shè)置為mainMOC
摄咆,采取這樣的兩層設(shè)計一般就能夠滿足大多數(shù)需求了凡蚜。
將耗時操作都放在backgroundMOC
中執(zhí)行,mainMOC
負責(zé)所有和UI
相關(guān)的操作吭从。所有和UI
無關(guān)的工作都交給backgroundMOC
番刊,在backgroundMOC
對數(shù)據(jù)發(fā)生改變后,調(diào)用save
方法會將改變push
到mainMOC
中影锈,再由mainMOC
執(zhí)行save
方法將改變保存到存儲區(qū)芹务。
代碼這里就不寫了,和上面例子中設(shè)置parentContext
代碼一樣鸭廷,主要講一下設(shè)計思路枣抱。
三層設(shè)計方案
但是我們發(fā)現(xiàn),上面的save
操作最后還是由mainMOC
去執(zhí)行的辆床,backgroundMOC
只是負責(zé)處理數(shù)據(jù)佳晶。雖然mainMOC
只執(zhí)行save
操作并不會很耗時,但是如果save
涉及的數(shù)據(jù)比較多讼载,這樣還是會對性能造成影響的轿秧。
雖然客戶端很少涉及到大量數(shù)據(jù)處理的需求中跌,但是假設(shè)有這樣的需求」酱郏可以考慮在兩層結(jié)構(gòu)之上漩符,給mainMOC
之上再添加一個parentMOC
,這個parentMOC
也是私有隊列的MOC
驱还,用于處理save
操作嗜暴。
這樣CoreData
存儲的結(jié)構(gòu)就是三層了,最底層是backgroundMOC
負責(zé)處理數(shù)據(jù)议蟆,中間層是mainMOC
負責(zé)UI
相關(guān)操作闷沥,最上層也是一個backgroundMOC
負責(zé)執(zhí)行save
操作。這樣就將影響UI
的所有耗時操作全都剝離到私有隊列中執(zhí)行咐容,使性能達到了很好的優(yōu)化舆逃。
需要注意的是,執(zhí)行MOC
相關(guān)操作時戳粒,不要阻塞當(dāng)前主線程路狮。所有MOC
的操作應(yīng)該是異步的,無論是子線程還是主線程享郊,盡量少的使用同步block
方法。
MOC同步時機
設(shè)置MOC
的parentContext
屬性之后孝鹊,parent
對于child
的改變是知道的炊琉,但是child
對于parent
的改變是不知道的。蘋果這樣設(shè)計又活,應(yīng)該是為了更好的數(shù)據(jù)同步苔咪。
Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:backgroundMOC];
emp.name = @"lxz";
emp.brithday = [NSDate date];
emp.height = @1.7f;
[backgroundMOC performBlock:^{
[backgroundMOC save:nil];
[mainMOC performBlock:^{
[mainMOC save:nil];
}];
}];
在上面這段代碼中,mainMOC
是backgroundMOC
的parentContext
柳骄。在backgroundMOC
執(zhí)行save
方法前团赏,backgroundMOC
和mainMOC
都不能獲取到Employee
的數(shù)據(jù),在backgroundMOC
執(zhí)行完save
方法后耐薯,自身上下文發(fā)生改變的同時舔清,也將改變push
到mainMOC
中,mainMOC
也具有了Employee
對象曲初。
所以在backgroundMOC
的save
方法執(zhí)行時体谒,是對內(nèi)存中的上下文做了改變,當(dāng)擁有PSC
的mainMOC
執(zhí)行save
方法后臼婆,是對本地存儲區(qū)做了改變抒痒。
好多同學(xué)都問我有Demo
沒有,其實文章中貼出的代碼組合起來就是個Demo
颁褂。后來想了想故响,還是給本系列文章配了一個簡單的Demo
傀广,方便大家運行調(diào)試,后續(xù)會給所有博客的文章都加上Demo
彩届。
Demo
只是來輔助讀者更好的理解文章中的內(nèi)容伪冰,應(yīng)該博客結(jié)合Demo
一起學(xué)習(xí),只看Demo
還是不能理解更深層的原理惨缆。Demo
中幾乎每一行代碼都會有注釋糜值,各位可以打斷點跟著Demo
執(zhí)行流程走一遍,看看各個階段變量的值坯墨。
Demo地址:劉小壯的Github
這兩天更新了一下文章寂汇,將CoreData
系列的六篇文章整合在一起,做了一個PDF
版的《CoreData Book》捣染,放在我Github上了骄瓣。PDF
上有文章目錄,方便閱讀耍攘。
如果你覺得不錯榕栏,請把PDF幫忙轉(zhuǎn)到其他群里,或者你的朋友蕾各,讓更多的人了解CoreData扒磁,衷心感謝!??