coredata的并發(fā)處理

coredata的并發(fā)處理

“我想要高可響應(yīng)性的app账锹,它允許我即使離線時(shí)候也能瀏覽數(shù)據(jù)”-我們常常聽(tīng)到有人這么說(shuō)授段。

諷刺的是,當(dāng)用coredata處理數(shù)據(jù)的時(shí)候恕齐,它會(huì)成為你的應(yīng)用的核心。除了管理你的app內(nèi)存中持有數(shù)據(jù)的方式巨缘,coredata還處理高級(jí)數(shù)據(jù)查詢(NSPredicate)娄周,懶加載(faults)端三,undo和redo支持谨履,表遷移害捕,UI整合(NSFetchedResultsController)躬它,合并策略以及其他相關(guān)事情。

Coredata可以是你的朋友菇夸,也可以是你的敵人鞠眉,這取決于你使用它的方式:如果在主線程處理,coredata會(huì)顯著的抑制程序性能。但是副渴,如果你想異步的使用coredata佑颇,你需要知道幾種模式宰闰。

Coredata設(shè)計(jì)

盡管有人說(shuō)coredata有比較陡的學(xué)習(xí)曲線胶背,不過(guò)一旦你理解了這個(gè)技術(shù)它就會(huì)變得很容易理解奔誓。這個(gè)技術(shù)就是關(guān)于如何管理你的模型的對(duì)象流和措。Coredata的結(jié)構(gòu)見(jiàn)下圖:

coredata_design-1024x521.png

通常SQLite數(shù)據(jù)庫(kù)是用作備份數(shù)據(jù)存儲(chǔ)派阱,但是你也能指定“原子性”或者“僅內(nèi)存”這兩種儲(chǔ)存類型贫母。要注意的是盡管在OSX上coredata支持XML數(shù)據(jù)存儲(chǔ)腺劣,但是在iOS上XML存儲(chǔ)并不可用橘原。

大致上,一個(gè)NSManagedObject實(shí)例可以被看做數(shù)據(jù)庫(kù)里表里的一條記錄吓懈。這個(gè)表的元數(shù)據(jù)被存儲(chǔ)在NSEntityDescriptor對(duì)象中耻警。NSManagedObjectContext是NSManagedObject對(duì)象們的數(shù)據(jù)池。它負(fù)責(zé)NSManagedObject的生命周期温兼,以及從底層數(shù)據(jù)存儲(chǔ)獲取數(shù)據(jù)對(duì)象募判,持久化,object faulting, undo/redo支持释液。

一個(gè)NSManagedObjectContext必須工作在一個(gè)隊(duì)列上误债∏薜福可通過(guò)兩種方式來(lái)達(dá)成箫老。第一種方式就是簡(jiǎn)單的只在創(chuàng)建NSManagedObjectContext的隊(duì)列中來(lái)使用它。這被稱作線程約束界斜。

dispatch_async(queue, ^{
    NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] init];
    // moc usage
});

第二種方式是將NSManagedObjectContext初始化為concurrency類型各薇,然后通過(guò)調(diào)用performBlock:^ 或者 performBlockAndWait:^來(lái)使用它峭判。如下:

NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateConcurrencyType];
[moc performBlock:^{
    // use moc in this block
}];

一共有三種異步類型可用:

  • NSConfinementConcurrencyType - 盡管很多人使用,但是這個(gè)類型已經(jīng)被蘋(píng)果官方淘汰了疗认。這個(gè)類型的意思是:你必須手動(dòng)保證MOC在創(chuàng)建它的隊(duì)列內(nèi)使用横漏。這種情況下缎浇,你應(yīng)該使用init方法來(lái)做初始化素跺,而不是initWithConcurrencyType方法刊愚。
  • NSPrivateQueueConcurrencyType - 意味著[moc performBlock:]將在后臺(tái)隊(duì)列中執(zhí)行。
  • NSMainQueueConcurrencyType - 意味著[moc performBlock:]將在主隊(duì)列中執(zhí)行仑乌。

NSManagedObject不會(huì)被記錄到持久化存儲(chǔ)中百拓,直到NSManagedObjectContext的save方法被調(diào)用。

NSPersistentStoreCoordinator的作用是綁定持久化存儲(chǔ)和NSManagedObjectContext晰甚。通常衙传,NSPersistentStoreCoordinator有一個(gè)持久化存儲(chǔ)和多個(gè)NSManagedObjectContext,不過(guò)厕九,它其實(shí)也可以同時(shí)處理多個(gè)持久化存儲(chǔ)蓖捶。你可以認(rèn)為NSPersistentStoreCoordinator是持久化存儲(chǔ)與NSManagedObject之間的中間層。

要清楚的是扁远,UI更新是在主線程中發(fā)生俊鱼,所以在主線程中的coredata操作都會(huì)產(chǎn)生影響,并且可能導(dǎo)致視覺(jué)問(wèn)題或者性能問(wèn)題。原因是所有UI處理,繪制和用戶事件(點(diǎn)擊,手勢(shì))都在主線程中進(jìn)行冕广。運(yùn)行任何其他事情在主線程上都會(huì)對(duì)app性能產(chǎn)生影響。 If you want to know more about why it is a “failed dream” to create multithreaded UI on any platform, read “Multithreaded toolkits: A failed dream?”.

處理coredata有多重不同的方式。下面是幾種實(shí)踐中比較通用的處理方案。

方案 #1

就是把所有東西都放在主線程中處理。

第一種方法是相當(dāng)簡(jiǎn)單的一個(gè):把所有事情放在主線程處理渣玲。你需要Xcode生成的默認(rèn)的CoreData堆棧。下圖是這個(gè)堆棧的樣子:

【圖片】

stack1-300x131.png

這個(gè)唯一的NSManagedObjectContext是被用來(lái)UI處理和記錄數(shù)據(jù)到數(shù)據(jù)庫(kù)中星掰。使用范例如下:

1.從服務(wù)器獲取數(shù)據(jù)

2.只要數(shù)據(jù)返回威始,就展現(xiàn)某種等待提示器

3.存儲(chǔ)獲得的數(shù)據(jù)到數(shù)據(jù)庫(kù)中(插入,更新,刪除)

4.隱藏等待提示器并展示新數(shù)據(jù)燎猛。

然而作為一個(gè)非常簡(jiǎn)單的解決方案愤钾,負(fù)面影響就是在數(shù)據(jù)庫(kù)記錄數(shù)據(jù)的時(shí)候,由于它使用主線程在數(shù)據(jù)庫(kù)記錄新數(shù)據(jù)占业,似的app不可用。幸運(yùn)的是该默,知名的MBProgressHUD庫(kù)以一種聰明的方式實(shí)現(xiàn)音榜,它能在主線程被coredata任務(wù)使用的時(shí)候在主線程展示動(dòng)畫(huà)霜第。

方案 #2

將后臺(tái)寫(xiě)任務(wù)從主線程中分離双仍。

【圖片】

stack2.png

這里翎卓,一個(gè)工作context被引入,用來(lái)存儲(chǔ)從服務(wù)端收到的數(shù)據(jù)矩肩。一旦數(shù)據(jù)存儲(chǔ)完成,主context可以以下面兩種方式與工作context進(jìn)行合并:

1.一旦工作context完成操作肛跌,它將通知主context,這之后工作線程可以通過(guò)調(diào)用reset方法來(lái)進(jìn)行重置稳捆。

2.工作context觸發(fā)mergeChangesFromContextDidSaveNotification通知后,主context將進(jìn)行合并块请。工作context運(yùn)行save方法后會(huì)觸發(fā)上述通知最岗。

這種方法的負(fù)面影響是開(kāi)發(fā)者需要手動(dòng)進(jìn)行context間的合并。

方案 #3

利用NSManagedObjectContext間的父子關(guān)系驮樊。

在iOS5上薇正,Apple在NSManagedObjectContext間引入了父子關(guān)系片酝。使用這個(gè)可以幫助同步context之間的數(shù)據(jù)。下面是一個(gè)可能的方案:

【圖片】

stack4-300x215.png

這個(gè)方案擁有在后臺(tái)隊(duì)列創(chuàng)建管理對(duì)象(NSManagedObject)的好處挖腰。但是在調(diào)用save方法后雕沿,所有數(shù)據(jù)對(duì)象將傳給父context(主隊(duì)列),而且真實(shí)的寫(xiě)數(shù)據(jù)庫(kù)將依舊在主線程中發(fā)生猴仑。所以盡管本方案比方案1略強(qiáng)审轮,但是它仍然對(duì)主線程有影響。

方案 #4

與方案3反向的管理對(duì)象上下文宁脊。

【圖片】

stack3-1024x354.png

流行庫(kù)Restkit就是使用的這種結(jié)構(gòu)断国,這此方案下,一個(gè)私有管理對(duì)象上下文被用于存儲(chǔ)從服務(wù)端獲取的數(shù)據(jù)榆苞。一旦完成,主context通過(guò)NSManagedObjectContextDidSaveNotification消息得到通知霞捡,于是主context更新到了它的父context(工作context)的數(shù)據(jù)坐漏。

在主context用于存儲(chǔ)數(shù)據(jù)時(shí)候(可以處理較小量的數(shù)據(jù)),由于在主context與工作context間的父子關(guān)系碧信,更改將被自動(dòng)合并到私有context赊琳。

方案 #5

擴(kuò)展父子關(guān)系的使用

【圖片】

stack5-300x298.png

這個(gè)方案很有趣。作為對(duì)于方案4的補(bǔ)充砰碴,本方案引入了一個(gè)新的并發(fā)context躏筏,這個(gè)context是主context的子context。接下來(lái)我們來(lái)看看對(duì)應(yīng)的數(shù)據(jù)流:

1.數(shù)據(jù)是異步獲取的呈枉,當(dāng)coredata在進(jìn)行處理時(shí)候用戶依然可以使用app趁尼。

2.服務(wù)端數(shù)據(jù)到達(dá)后,工作context被用來(lái)在數(shù)據(jù)庫(kù)中存儲(chǔ)數(shù)據(jù)猖辫。既然存儲(chǔ)發(fā)生在私有隊(duì)列酥泞,所以用戶依然可以和之前一樣使用app。

3.在工作context調(diào)用save方法后啃憎,數(shù)據(jù)就會(huì)被合并到主context≈ザ冢現(xiàn)在這個(gè)合并是在主隊(duì)列發(fā)生的,但是由于合并是在內(nèi)存中完成的辛萍,所以這是并不會(huì)產(chǎn)生嚴(yán)重的性能問(wèn)題悯姊。

4.最終,數(shù)據(jù)被傳遞到另一個(gè)工作context贩毕,由這個(gè)工作context來(lái)真實(shí)的把數(shù)據(jù)寫(xiě)到存儲(chǔ)里悯许。

這種方法的負(fù)面影響是,由于有3個(gè)context進(jìn)行合并耳幢,所有這些合并工作對(duì)app性能有一個(gè)可察覺(jué)的負(fù)面影響岸晦。

方案 #6

每個(gè)controller里都有一個(gè)管理對(duì)象context

如果你有很多工作context或者他們存儲(chǔ)了大量的數(shù)據(jù)欧啤,主context與這些工作context間的數(shù)據(jù)合并就發(fā)生在主線程,這可能導(dǎo)致app不響應(yīng)启上。

另外你辣,主context在持有很多數(shù)據(jù)時(shí)候會(huì)變得很重。隨著時(shí)間繼續(xù)尚揣,對(duì)象會(huì)不停的加入context中站玄。如果你想清除它,你必須在主context中調(diào)用reset方法包券。但是纫谅,如果你有多個(gè)視圖控制器在屏幕上課件,這種情況在iPad上比較常見(jiàn)溅固,你就需要對(duì)這個(gè)reset操作小心謹(jǐn)慎了付秕,以免某個(gè)視圖控制器獲得到已經(jīng)被重置的數(shù)據(jù)。

【圖片】

stack6.png

這個(gè)解決方案就是你簡(jiǎn)單的不合并任何事情到主context中侍郭。你不一定只能有一個(gè)主context询吴!與所有view controller只有一個(gè)主context相反,你可以嘗試這種方法:每個(gè)view controller都有自己的NSManagedObjectContext亮元。方法如下:

1.當(dāng)數(shù)據(jù)從服務(wù)器獲得后猛计,使用工作context來(lái)存儲(chǔ)數(shù)據(jù)。

2.一旦存儲(chǔ)完成爆捞,就觸發(fā)一個(gè)通知奉瘤,比如類似MyAppUsersSavedNotification(假設(shè)你正在存儲(chǔ)user列表數(shù)據(jù)到數(shù)據(jù)庫(kù)中)。

3.顯示user列表的控制器MyAppUsersViewController收到消息并執(zhí)行下述代碼:

[self.managedObjectContext reset];
self.fetchedResultsController = nil;
[self.tableView reloadData];

這樣煮甥,這個(gè)view controller的NSFetchedResultsController將會(huì)被重新創(chuàng)建(很多例子中盗温,假設(shè)你也有懶獲取器),它會(huì)從coredata獲取最新的數(shù)據(jù)苛秕。盡管這個(gè)操作是在主隊(duì)列中進(jìn)行的肌访,但這應(yīng)該是相當(dāng)快速的,只要?jiǎng)e忘記設(shè)置NSFetchRequest上fetchBatchSize艇劫。另外吼驶,也要考慮你真實(shí)需要的數(shù)據(jù),如果你不需要所有字段店煞,那么你應(yīng)該只使用NSFetchRequest中的propertiesToFetch字段來(lái)獲取你需要的字段蟹演。

哪個(gè)更好?

也許你希望找到一種適用所有情況的解決方案顷蟀。不幸的是酒请,通常來(lái)說(shuō),最佳方案取決于你的需求鸣个。對(duì)于簡(jiǎn)單的使用場(chǎng)景來(lái)說(shuō)你使用方案1即可羞反,但是對(duì)于一些更重的使用場(chǎng)景布朦,我們建議你使用RestKit和那些采用了方案4的解決方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昼窗,一起剝皮案震驚了整個(gè)濱河市是趴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌澄惊,老刑警劉巖唆途,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掸驱,居然都是意外死亡肛搬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門毕贼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)温赔,“玉大人,你說(shuō)我怎么就攤上這事鬼癣∪酶梗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵扣溺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瓜晤,道長(zhǎng)锥余,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任痢掠,我火速辦了婚禮驱犹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘足画。我一直安慰自己雄驹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布淹辞。 她就那樣靜靜地躺著医舆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪象缀。 梳的紋絲不亂的頭發(fā)上蔬将,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音央星,去河邊找鬼霞怀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛莉给,可吹牛的內(nèi)容都是我干的毙石。 我是一名探鬼主播廉沮,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼徐矩!你這毒婦竟也來(lái)了滞时?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤丧蘸,失蹤者是張志新(化名)和其女友劉穎漂洋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體力喷,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刽漂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弟孟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贝咙。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拂募,靈堂內(nèi)的尸體忽然破棺而出庭猩,到底是詐尸還是另有隱情,我是刑警寧澤陈症,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布蔼水,位于F島的核電站,受9級(jí)特大地震影響录肯,放射性物質(zhì)發(fā)生泄漏趴腋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一论咏、第九天 我趴在偏房一處隱蔽的房頂上張望优炬。 院中可真熱鬧,春花似錦厅贪、人聲如沸蠢护。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)葵硕。三九已至,卻和暖如春单寂,著一層夾襖步出監(jiān)牢的瞬間贬芥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工宣决, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蘸劈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓尊沸,卻偏偏與公主長(zhǎng)得像威沫,于是被迫代替她去往敵國(guó)和親贤惯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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