這是蘋(píng)果官方文檔 Core Data Programming Guide 的渣翻譯。
Faulting可以降低通過(guò)在持久化存儲(chǔ)中保留占位對(duì)象(faults)的方式來(lái)降低應(yīng)用內(nèi)存的使用。有一個(gè)叫uniquing的相關(guān)特性能確保上述功能,在一個(gè)既定的托管對(duì)象上下文中抬闯,一條既定記錄只會(huì)有一個(gè)托管對(duì)象。
Faulting限制了對(duì)象圖的大小
托管對(duì)象代表了持久化存儲(chǔ)中的數(shù)據(jù)关筒。在某些情況下托管對(duì)象可能是一個(gè)fault —— 一個(gè)還沒(méi)有從外部數(shù)據(jù)存儲(chǔ)中加載到屬性值的對(duì)象溶握。Fualting降低了應(yīng)用的內(nèi)存消耗。一個(gè)fault是一個(gè)表示了一個(gè)尚未完全實(shí)現(xiàn)的托管對(duì)象蒸播,或是一個(gè)表示了一個(gè)關(guān)系的集合對(duì)象睡榆。
- 一個(gè)托管對(duì)象fault是一個(gè)相應(yīng)類(lèi)的實(shí)例,但是它的持久化變量還沒(méi)初始化袍榆。
- 一個(gè)關(guān)系fault是表示了這個(gè)關(guān)系的集合類(lèi)的子類(lèi)胀屿。
Faulting允許Core Data在對(duì)象圖上放置邊界。因?yàn)橐粋€(gè)fault是沒(méi)有被實(shí)現(xiàn)的包雀,一個(gè)托管對(duì)象fault消耗更少的內(nèi)存宿崭,并且關(guān)聯(lián)到一個(gè)fault的托管對(duì)象完全不需要表示在內(nèi)存中。
為了說(shuō)明這一點(diǎn)馏艾,可以想象一個(gè)應(yīng)用允許一個(gè)用戶去查詢并編輯一個(gè)“雇員”的詳情劳曹。這個(gè)“雇員”有一個(gè)關(guān)系是指向一個(gè)“經(jīng)理”和一個(gè)關(guān)系指向一個(gè)“部門(mén)”,并且這些對(duì)象反過(guò)來(lái)也有其他關(guān)系琅摩。如果你在持久化存儲(chǔ)中僅僅查詢一個(gè)“雇員”,它的“經(jīng)理”锭硼、“部門(mén)”和“報(bào)告”關(guān)系都是用faults初始化表示的房资。圖13-1展示了使用fault來(lái)表示的“部門(mén)”關(guān)系。
圖13-1 一個(gè)使用fault表示的“部門(mén)”
雖然這個(gè)fault是一個(gè)“部門(mén)”類(lèi)的實(shí)例檀头,但是它并沒(méi)有被實(shí)現(xiàn) —— 任何一個(gè)它的持久化實(shí)例變量都還沒(méi)被設(shè)定轰异。這就意味著不單單“部門(mén)”對(duì)象消耗更好的內(nèi)存岖沛,并且不需要填充它的“雇員”關(guān)系。如果需要讓對(duì)象圖變得完整搭独,那就編輯一個(gè)“雇員”的一個(gè)屬性婴削,它最后就會(huì)去創(chuàng)建對(duì)象來(lái)表示整個(gè)結(jié)構(gòu)。
Firing Faults
Fault處理是直接的 —— 你不需要執(zhí)行任何查詢來(lái)實(shí)現(xiàn)一個(gè)fault牙肝。如果在某些場(chǎng)合一個(gè)fault對(duì)象的持久化屬性被訪問(wèn)到了唉俗,那么Core Data就會(huì)自動(dòng)查詢數(shù)據(jù)來(lái)初始化對(duì)象。這個(gè)過(guò)程一般被稱(chēng)為“fire the fault”配椭。如果你訪問(wèn)“部門(mén)”對(duì)象上的一個(gè)屬性虫溜,例如“名字”,那么這個(gè)fault就會(huì)被fire —— 并且在這種情況Core Data會(huì)為你執(zhí)行一個(gè)查詢來(lái)檢索到所有的對(duì)象屬性股缸。
Core Data在一個(gè)fault的一個(gè)持久化屬性(例如“名字”)被訪問(wèn)到的時(shí)候fire fault衡楞。然而,一個(gè)個(gè)地fire fault是十分低效的敦姻,并且有更好的策略來(lái)從持久化存儲(chǔ)中拿到數(shù)據(jù)(參考Decreasing Fault Overhead)瘾境。想要更高效處理fault和關(guān)系,可以參考Fetching Managed Objects和Preventing a Fault from Firing镰惦。
當(dāng)一個(gè)fault被fire寄雀,如果這個(gè)數(shù)據(jù)在緩存中可用Core Data不會(huì)再去存儲(chǔ)中獲取。使用緩存命中陨献,轉(zhuǎn)換一個(gè)fault為一個(gè)已實(shí)現(xiàn)的托管對(duì)象是非澈杏蹋快的 —— 就像普通的托管對(duì)象被實(shí)例化一樣。如果這個(gè)數(shù)據(jù)不在緩存中可用眨业,Core Data會(huì)自動(dòng)為這個(gè)fault執(zhí)行查詢急膀;這就能從持久化存儲(chǔ)中獲取數(shù)據(jù),并再次放進(jìn)內(nèi)存中龄捡。
一個(gè)對(duì)象是否是fault簡(jiǎn)單地意味著一個(gè)既定的托管對(duì)象是否擁有已被包裝好的持久化屬性和已經(jīng)準(zhǔn)備好被使用卓嫂。如果你需要判斷一個(gè)對(duì)象是否是fault,可以調(diào)用它的“isFault”方法聘殖,這個(gè)方法不會(huì)fire這個(gè)fault(沒(méi)有訪問(wèn)任何關(guān)系或?qū)傩裕┏况āH绻癷sFault”返回“NO”鳖谈,那么這個(gè)數(shù)據(jù)應(yīng)該存在內(nèi)存中并且不是fault帝际。然后,如果“isFault”返回“YES”跑芳,并不能簡(jiǎn)單認(rèn)為這個(gè)數(shù)據(jù)不在內(nèi)存中突照。這個(gè)數(shù)據(jù)可能存在內(nèi)存中帮非,或者不存在,取決于許多影響到緩存機(jī)制的因素。
雖然“description”方法不會(huì)引起fault被fire末盔,但是如果你實(shí)現(xiàn)了一個(gè)自定義的“decription”方法并訪問(wèn)了對(duì)象的持久化屬性筑舅,那么這個(gè)fault會(huì)被fire。強(qiáng)烈不建議你用此方式覆寫(xiě)“description”陨舱。
不可以根據(jù)需要只加載托管對(duì)象中的某些屬性翠拣,而不實(shí)現(xiàn)整個(gè)對(duì)象(查詢所有屬性值)。關(guān)于如何處理大數(shù)據(jù)屬性游盲,參考 Binary Large Data Objects (BLOBs)误墓。
轉(zhuǎn)換對(duì)象為Fault
轉(zhuǎn)換一個(gè)已實(shí)現(xiàn)的對(duì)象為fault對(duì)于整理對(duì)象圖來(lái)說(shuō)是十分有用的,也能確保屬性值是同步的背桐。轉(zhuǎn)換一個(gè)托管對(duì)象為fault釋放不必要的內(nèi)存优烧,并設(shè)置它的內(nèi)存屬性值為nil。(參考Reducing Memory Overhead并保證數(shù)據(jù)時(shí)最新的)链峭。
你可以使用"refreshObject:mergeChanges:"方法轉(zhuǎn)換一個(gè)已實(shí)現(xiàn)對(duì)象為fault畦娄。如果你傳遞"NO"作為“mergeChanges”參數(shù),你必須確保這個(gè)對(duì)象的關(guān)系沒(méi)有變更弊仪。否則熙卡,當(dāng)你保存了這個(gè)上下文的時(shí)候,你會(huì)引發(fā)持久化存儲(chǔ)的引用的完整性問(wèn)題励饵。
當(dāng)一個(gè)對(duì)象轉(zhuǎn)變成為了fault驳癌,它的"didTurnIntoFault"方法就會(huì)被調(diào)用。你可能實(shí)現(xiàn)了一個(gè)自定義“didTurnIntoFault”方法來(lái)實(shí)現(xiàn)一些清理工作功能役听。例如颓鲜,保證數(shù)據(jù)是最新的。
注意
Core Data避免使用屬于“unfaulting”因?yàn)樗哂忻曰笮缘溆琛2荒苷f(shuō)“unfaulting”一個(gè)虛擬內(nèi)存事件fault甜滨。
事件fault可以是被觸發(fā)、引發(fā)瘤袖、fire(激發(fā)衣摩?)或者碰到。
當(dāng)然捂敌,你可以通過(guò)不同的方法來(lái)釋放內(nèi)存艾扮。Core Data稱(chēng)之為轉(zhuǎn)換一個(gè)對(duì)象為fault。
Fault和KVO通知
當(dāng)Core Data轉(zhuǎn)換一個(gè)對(duì)象為fault占婉,key-value observing(KVO)變更通知就會(huì)發(fā)送到這個(gè)對(duì)象的屬性上泡嘴。如果你正在觀察一個(gè)被轉(zhuǎn)換成了fault的對(duì)象的屬性,并且這個(gè)fault突然被實(shí)現(xiàn)化了锐涯,你會(huì)收到其實(shí)值并沒(méi)有真正改變的屬性的變更通知磕诊。參考Key-Value Observing Programming Guide。
雖然這些值從你的角度來(lái)看并沒(méi)有發(fā)生改變纹腌,但是在內(nèi)存中隨著對(duì)象的具現(xiàn)化霎终,其實(shí)每個(gè)字節(jié)都發(fā)生了改變。KVO機(jī)制要求無(wú)論在任何時(shí)候只要根據(jù)對(duì)比指針判斷值發(fā)生了改變升薯,Core Data都要發(fā)出通知莱褒。KVO需要這些通知來(lái)跟蹤跨key path和依賴(lài)對(duì)象的改變。
Uniquing確保一個(gè)托管對(duì)象對(duì)應(yīng)一個(gè)上下文中的一條記錄
Core Data確保涎劈,在一個(gè)托管對(duì)象上下文广凸,一個(gè)持久化存儲(chǔ)中的實(shí)體是和唯一一個(gè)托管對(duì)象關(guān)聯(lián)的。這個(gè)技術(shù)就是uniquing蛛枚。如果沒(méi)有uniquing谅海,你可能要無(wú)休止處理一個(gè)上下文中超過(guò)一個(gè)托管對(duì)象表示同一條記錄。
例如蹦浦,試想如圖13-2展示的情景扭吁;兩個(gè)“雇員”被加載到了一個(gè)托管對(duì)象上下文中。每個(gè)“雇員”都有一個(gè)"部門(mén)"關(guān)系盲镶,但是這時(shí)候“部門(mén)”是fault侥袜。
圖13-2 獨(dú)立的“部門(mén)”對(duì)象fault
這看起來(lái)就是每個(gè)“雇員”都有一個(gè)獨(dú)立的“部門(mén)”,并且如果你調(diào)用每個(gè)“雇員”的“部門(mén)” —— 轉(zhuǎn)換“部門(mén)”fault稱(chēng)為普通對(duì)象 —— 你就會(huì)得到兩個(gè)在內(nèi)存中獨(dú)立的“部門(mén)”溉贿。然而枫吧,如果兩個(gè)“雇員”都屬于同一個(gè)“部門(mén)”(例如,市場(chǎng)部)宇色,然后Core Data要確保在一個(gè)既定的托管對(duì)象上下文中只有一個(gè)對(duì)象表示“市場(chǎng)部”九杂。如果兩個(gè)“雇員”都屬于同一個(gè)部門(mén),他們的“部門(mén)”關(guān)系都應(yīng)該引用同一個(gè)fault宣蠕,就如圖13-3展示的那樣例隆。
圖13-3 表示兩個(gè)“雇員”共同工作的“部門(mén)”的唯一fault
如果沒(méi)有uniquing,當(dāng)你獲取每個(gè)“雇員”并且調(diào)用“部門(mén)”的時(shí)候 —— 從而要fire每個(gè)對(duì)應(yīng)的fault —— 一個(gè)新的“部門(mén)”對(duì)象每次都會(huì)被創(chuàng)建出來(lái)植影。這樣會(huì)導(dǎo)致出現(xiàn)很多對(duì)象裳擎,每個(gè)都表示同一個(gè)“部門(mén)”,可能會(huì)導(dǎo)致不同步的思币、沖突的數(shù)據(jù)鹿响。當(dāng)上下文保存的時(shí)候,就不能保證正確的數(shù)據(jù)能夠提交到存儲(chǔ)中谷饿。
更通俗地說(shuō)惶我,在一個(gè)既定的上下文中,所有表示“市場(chǎng)部”的托管對(duì)象都指向同一個(gè)實(shí)例 —— 他們擁有相同的“市場(chǎng)部”數(shù)據(jù)視圖 —— 即使這個(gè)“市場(chǎng)部”是個(gè)fault博投。
注意
這次討論主要是圍繞單個(gè)托管對(duì)象上下文的绸贡。每一個(gè)托管對(duì)象上下文都表示一個(gè)不同的數(shù)據(jù)視圖。
如果同一個(gè)“雇員”加載到了第二個(gè)上下文,那么他們 —— 并且對(duì)應(yīng)的所有“部門(mén)”對(duì)象 —— 都在對(duì)象中表現(xiàn)成不同的對(duì)象听怕。
在不同的上下文的中對(duì)象可能存在不同的捧挺、沖突的數(shù)據(jù)。這恰恰是Core Data架構(gòu)的任務(wù)尿瞭,同時(shí)去檢測(cè)闽烙、解決這些沖突。