?? ? 鍵值觀察是一種機制,允許將其他對象的指定屬性的更改通知對象。
?? ? 重要:為了理解鍵值觀察锰扶,您必須首先理解鍵值編碼。
?? ? 鍵值觀察提供了一種機制蠢莺,允許將其他對象的特定屬性的更改通知對象。它對于應(yīng)用程序中模型層和控制器層之間的通信特別有用零如。(在OS X中躏将,控制器層綁定技術(shù)嚴重依賴于鍵值觀察。)控制器對象通常觀察模型對象的屬性考蕾,視圖對象通過控制器觀察模型對象的屬性祸憋。此外,模型對象可以觀察其他模型對象(通常用于確定依賴值何時發(fā)生變化)肖卧,甚至可以觀察自身(再次用于確定依賴值何時發(fā)生變化)蚯窥。
?? ? 您可以觀察屬性,包括簡單屬性塞帐、一對一關(guān)系和多對多關(guān)系拦赠。許多關(guān)系的觀察者被告知所做變更的類型以及變更中涉及到的對象。
?? ? 一個簡單的示例演示了KVO如何在應(yīng)用程序中發(fā)揮作用葵姥。假設(shè)Person對象與Account對象交互荷鼠,表示該對象在銀行的儲蓄帳戶。Person的實例可能需要知道Account實例的某些方面何時發(fā)生變化牌里,比如余額或利率颊咬。
?? ? 如果這些屬性是Account的公共屬性务甥,那么用戶可以定期輪詢帳戶以發(fā)現(xiàn)更改牡辽,但這當然是低效的喳篇,而且通常不切實際。更好的方法是使用KVO态辛,它類似于在發(fā)生更改時接收中斷的人麸澜。
?? ? 要使用KVO,首先必須確保所觀察的對象(本例中的帳戶)符合KVO奏黑。通常炊邦,如果您的對象繼承自NSObject,并且您以通常的方式創(chuàng)建屬性熟史,那么您的對象及其屬性將自動與KVO兼容馁害。手動實現(xiàn)遵從性也是可能的。KVO遵從性描述了自動和手動鍵值觀察之間的區(qū)別蹂匹,以及如何實現(xiàn)這兩者碘菜。
?? ? 接下來,您必須注冊您的觀察者實例(Person)和觀察到的實例(Account)限寞。Person向帳戶發(fā)送一個addObserver:forKeyPath:options:context: message忍啸,每個觀察到的密鑰路徑發(fā)送一次,并將其命名為observer履植。
?? ? 為了從帳戶接收更改通知计雌,Person實現(xiàn)了所有觀察者都需要的observeValueForKeyPath:ofObject:change:context: method。一旦注冊的密鑰路徑發(fā)生更改玫霎,該帳戶將向該人發(fā)送此消息凿滤。然后,該人可以根據(jù)變更通知采取適當?shù)男袆印?/p>
?? ? 最后庶近,當不再需要通知時(至少在解除分配之前)翁脆,Person實例必須通過發(fā)送消息removeObserver:forKeyPath:到帳戶來注銷注冊。
?? ? 注冊鍵值觀察描述了注冊拦盹、接收和注銷鍵值觀察通知的完整生命周期鹃祖。
?? ? KVO的主要好處是,您不必實現(xiàn)自己的方案來在每次屬性更改時發(fā)送通知普舆。它定義良好的基礎(chǔ)架構(gòu)具有框架級支持恬口,這使得它易于采用——通常您不需要向項目中添加任何代碼。此外沼侣,基礎(chǔ)設(shè)施已經(jīng)具備了完整的功能祖能,這使得支持單個屬性的多個觀察者和依賴值變得很容易。
?? ? 注冊依賴鍵說明如何指定一個鍵的值依賴于另一個鍵的值蛾洛。
?? ? 與使用NSNotificationCenter的通知不同养铸,沒有為所有觀察者提供更改通知的中心對象雁芙。相反,當發(fā)生更改時钞螟,通知將直接發(fā)送到觀察對象兔甘。NSObject提供了鍵值觀察的基本實現(xiàn),您應(yīng)該很少需要覆蓋這些方法鳞滨。
?? ? 注冊鍵值觀察
?? ? 您必須執(zhí)行以下步驟洞焙,以使對象能夠接收符合kvo的屬性的鍵值觀察通知:
?? ? 1.使用addObserver:forKeyPath:options:context:方法向被觀察對象注冊觀察者。
?? ? 2.在觀察者內(nèi)部實現(xiàn)observeValueForKeyPath:ofObject:change:context:接受變更通知消息拯啦。
?? ? 3.使用removeObserver:forKeyPath方法注銷觀察者的注冊:當它不再應(yīng)該接收消息時澡匪。至少,在觀察者從內(nèi)存中釋放之前調(diào)用這個方法褒链。
?? ? 重要:并不是所有的類都與kvo兼容唁情。通過遵循KVO遵從性中描述的步驟,您可以確保您自己的類符合KVO甫匹。通常甸鸟,蘋果提供的框架中的屬性只有在這樣的文檔中才符合kvo。
?? ? 注冊為觀察者
?? ? 觀察對象首先通過發(fā)送addObserver:forKeyPath:options:context: message向觀察對象注冊自己赛惩,并將自己作為觀察者和要觀察的屬性的鍵路徑進行傳遞哀墓。觀察者還指定一個選項參數(shù)和一個上下文指針來管理通知的各個方面
?? ? 選項
?? ? 選項參數(shù)(以位或選項常量的形式指定)影響通知中提供的更改字典的內(nèi)容和生成通知的方式。
?? ? 通過指定選項NSKeyValueObservingOptionOld喷兼,您可以選擇從更改之前接收被觀察屬性的值篮绰。使用選項NSKeyValueObservingOptionNew請求屬性的新值。通過按位或這些選項中的一個季惯,可以同時接收新值和舊值吠各。
?? ? 指示被觀察對象使用NSKeyValueObservingOptionInitial選項發(fā)送一個即時更改通知(在addObserver:forKeyPath:options:context: returns之前)。您可以使用此附加的一次性通知在觀察者中建立屬性的初始值勉抓。
?? ? 通過包含選項NSKeyValueObservingOptionPrior贾漏,可以指示被觀察對象在屬性更改之前(除了通常的更改之后的通知之外)發(fā)送通知。更改字典通過包含NSNumber包裝YES值的鍵NSKeyValueChangeNotificationIsPriorKey來表示更改前通知藕筋。否則該鍵不存在纵散。當觀察者自己的KVO遵從性要求它為依賴于被觀察屬性的屬性之一調(diào)用-willChange…方法時,您可以使用prechange通知隐圾。通常的更改后通知來得太晚伍掀,無法及時調(diào)用willChange。
?? ? 上下文
?? ? 消息中addObserver:forKeyPath:options:context: message中的上下文指針包含任意數(shù)據(jù)暇藏,這些數(shù)據(jù)將在相應(yīng)的更改通知中傳遞回觀察者蜜笤。您可以指定NULL并完全依賴于鍵路徑字符串來確定更改通知的來源,但是這種方法可能會為其超類由于不同原因也在觀察相同鍵路徑的對象帶來問題盐碱。
?? ? 一種更安全把兔、更可擴展的方法是使用上下文來確保接收到的通知是針對觀察者而不是超類的沪伙。
?? ? 類中唯一命名的靜態(tài)變量的地址是一個很好的上下文。在超類或子類中以類似方式選擇的上下文不太可能重疊县好。您可以為整個類選擇一個上下文围橡,并依賴于通知消息中的鍵路徑字符串來確定更改了什么∑傅耄或者某饰,您可以為每個觀察到的鍵路徑創(chuàng)建不同的上下文儒恋,這完全避免了字符串比較的需要善绎,從而產(chǎn)生更高效的通知解析。清單1顯示了以這種方式選擇的balance和interestRate屬性的示例上下文诫尽。
?? ? 清單1創(chuàng)建上下文指針
?? ? static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
?? ? static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;
?? ? 清單2中的示例演示了Person實例如何使用給定的上下文指針將自己注冊為Account實例的balance和interestRate屬性的觀察者禀酱。
?? ? 清單2將檢查器注冊為balance和interestRate屬性的觀察者
?? ? - (void)registerAsObserverForAccount:(Account*)account {
?? ? [account addObserver:self
?? ? forKeyPath:@"balance"
?? ? options:(NSKeyValueObservingOptionNew |
?? ? NSKeyValueObservingOptionOld)
?? ? context:PersonAccountBalanceContext];
?? ? [account addObserver:self
?? ? forKeyPath:@"interestRate"
?? ? options:(NSKeyValueObservingOptionNew |
?? ? NSKeyValueObservingOptionOld)
?? ? context:PersonAccountInterestRateContext];
?? ? }
?? ? 注意:觀察addObserver:forKeyPath:options:context:方法的鍵值不維護對觀察對象、被觀察對象或上下文的強引用牧嫉。您應(yīng)該確保在必要時維護對觀察對象剂跟、被觀察對象和上下文的強引用。
?? ? 接收更改通知
?? ? 當對象的被觀察屬性的值發(fā)生變化時酣藻,觀察者接收到一個observeValueForKeyPath:ofObject:change:context: message曹洽。所有觀察者都必須實現(xiàn)這個方法。
?? ? 觀察對象提供觸發(fā)通知的鍵路徑辽剧、本身作為相關(guān)對象送淆、包含更改細節(jié)的字典以及在為該鍵路徑注冊觀察者時提供的上下文指針。
?? ? change dictionary條目NSKeyValueChangeKindKey提供關(guān)于發(fā)生的更改類型的信息怕轿。如果所觀察對象的值發(fā)生了更改偷崩,NSKeyValueChangeKindKey條目將返回NSKeyValueChangeSetting。根據(jù)觀察者注冊時指定的選項撞羽,更改字典中的NSKeyValueChangeOldKey和NSKeyValueChangeNewKey條目包含更改之前和之后的屬性值阐斜。如果屬性是對象,則直接提供值诀紊。如果屬性是標量或C結(jié)構(gòu)谒出,則將值包裝在NSValue對象中(與鍵值編碼一樣)。
?? ? 如果觀察到的屬性是一對多關(guān)系邻奠,NSKeyValueChangeKindKey條目還指示關(guān)系中的對象是否分別被插入笤喳、刪除或替換為返回nskeyvaluechangeinsert、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement惕澎。
?? ? NSKeyValueChangeIndexesKey的更改字典條目是一個NSIndexSet對象莉测,它指定了更改關(guān)系中的索引。如果NSKeyValueObservingOptionNew或NSKeyValueObservingOptionOld在觀察者注冊時被指定為選項唧喉,那么變更字典中的NSKeyValueChangeOldKey和NSKeyValueChangeNewKey條目就是數(shù)組捣卤,數(shù)組中包含了在變更前后相關(guān)對象的值忍抽。
?? ? 清單3中的示例顯示了Person觀察者的observeValueForKeyPath:ofObject:change:context: implementation,它記錄了balance和interestRate屬性的新舊值董朝,如清單2中所注冊的那樣鸠项。
?? ? 清單3實現(xiàn)了observeValueForKeyPath:ofObject:change:context:
?? ? - (void)observeValueForKeyPath:(NSString *)keyPath
?? ? ofObject:(id)object
?? ? change:(NSDictionary *)change
?? ? context:(void *)context {
?? ? if (context == PersonAccountBalanceContext) {
?? ? //做一些Balance的事情……
?? ? } else if (context == PersonAccountInterestRateContext) {
?? ? //做一些interest rate…的事情
?? ? } else {
?? ? // 任何未識別的上下文都必須屬于super
?? ? [super observeValueForKeyPath:keyPath
?? ? ofObject:object
?? ? change:change
?? ? context:context];
?? ? }
?? ? }
?? ? 如果在注冊觀察者時指定了空上下文,則將通知的鍵路徑與正在觀察的鍵路徑進行比較子姜,以確定發(fā)生了什么更改祟绊。如果對所有觀察到的鍵路徑使用單一上下文,則首先根據(jù)通知的上下文對其進行測試哥捕,并找到匹配項牧抽,然后使用鍵路徑字符串比較來確定具體更改了什么。如果您為每個鍵路徑提供了惟一的上下文(如本文所示)遥赚,那么一系列簡單的指針比較將同時告訴您該通知是否針對該觀察者扬舒,如果是,則告訴您哪些鍵路徑發(fā)生了更改凫佛。
?? ? 在任何情況下讲坎,當觀察者不識別上下文(或者在簡單的情況下,任何關(guān)鍵路徑)時愧薛,它應(yīng)該總是調(diào)用超類的observeValueForKeyPath:ofObject:change:context:的實現(xiàn)晨炕,因為這意味著超類也注冊了通知。
?? ? 注意:如果通知傳播到類層次結(jié)構(gòu)的頂部毫炉,NSObject將拋出nsinternalin一致性異常瓮栗,因為這是一個編程錯誤:子類未能使用它注冊的通知。
?? ? 作為觀察者刪除一個對象
?? ? 通過向觀察對象發(fā)送removeObserver:forKeyPath:context: message碘箍,指定觀察對象遵馆、鍵路徑和上下文,可以刪除鍵值觀察者丰榴。清單4中的示例顯示Person將自己作為balance和interestRate的觀察者刪除货邓。
?? ? 清單4將檢查器作為balance和interestRate的觀察者刪除
?? ? - (void)unregisterAsObserverForAccount:(Account*)account {
?? ? [account removeObserver:self
?? ? forKeyPath:@"balance"
?? ? context:PersonAccountBalanceContext];
?? ? [account removeObserver:self
?? ? forKeyPath:@"interestRate"
?? ? context:PersonAccountInterestRateContext];
?? ? }
?? ? 接收到removeObserver:forKeyPath:context: message后,觀察對象將不再接收指定鍵路徑和對象的observeValueForKeyPath:ofObject:change:context: messages四濒。
?? ? 在移除觀察者時换况,請記住以下幾點:
?? ? 1.如果沒有注冊為觀察者,請求以觀察者的身份被刪除會導(dǎo)致NSRangeException異常盗蟆。您可以調(diào)用removeObserver:forKeyPath:context:對addObserver:forKeyPath:options:context:的對應(yīng)調(diào)用一次戈二,或者如果在您的應(yīng)用程序中不可行,則將removeObserver:forKeyPath:context: call放在try/catch塊中喳资,以處理潛在的異常觉吭。
?? ? 2.解除分配時,觀察者不會自動刪除自身仆邓。被觀察對象繼續(xù)發(fā)送通知鲜滩,而不注意觀察者的狀態(tài)伴鳖。但是,與發(fā)送到已釋放對象的任何其他消息一樣徙硅,更改通知會觸發(fā)內(nèi)存訪問異常榜聂。因此,您要確保觀察者在從內(nèi)存中消失之前刪除自己嗓蘑。
?? ? 3.協(xié)議沒有提供詢問對象是否是觀察者或被觀察者的方法须肆。構(gòu)造代碼以避免發(fā)布相關(guān)錯誤。典型的模式是在觀察者的初始化過程中注冊為觀察者(例如在init或viewDidLoad中)桩皿,在解除分配過程中取消注冊(通常在dealloc中)豌汇,確保正確地對和有序地添加和刪除消息,并且在觀察者從內(nèi)存中釋放之前取消注冊业簿。
?? ? 為了被認為符合特定屬性的kvo瘤礁,類必須確保以下內(nèi)容:
?? ? 1.類必須是符合屬性的鍵值編碼,如在確保KVC遵從性中指定的那樣梅尤。
?? ? KVO支持與KVC相同的數(shù)據(jù)類型,包括Objective-C對象以及標量和結(jié)構(gòu)支持中列出的標量和結(jié)構(gòu)岩调。
?? ? 2.該類為屬性發(fā)出KVO更改通知巷燥。
?? ? 3.適當?shù)刈砸蕾囨I。
?? ? 有兩種技術(shù)可以確保發(fā)出更改通知号枕。NSObject提供了自動支持缰揪,默認情況下,對符合鍵值編碼的類的所有屬性都可用葱淳。通常钝腺,如果您遵循標準的Cocoa編碼和命名約定,您可以使用自動更改通知—您不需要編寫任何額外的代碼赞厕。
?? ? 手動更改通知提供了對何時發(fā)出通知的額外控制艳狐,并且需要額外的編碼。您可以通過實現(xiàn)類方法automaticallyNotifiesObserversForKey:來控制子類屬性的自動通知皿桑。
?? ? 自動更改通知
?? ? NSObject提供了自動鍵值更改通知的基本實現(xiàn)毫目。自動鍵值更改通知通知觀察者使用符合鍵值的訪問器以及鍵值編碼方法所做的更改。由mutableArrayValueForKey:返回的集合代理對象也支持自動通知诲侮。
?? ? 清單1所示的示例會將更改通知屬性名的任何觀察者镀虐。
?? ? 清單1發(fā)出KVO更改通知的方法調(diào)用示例
?? ? // 調(diào)用訪問器方法。
?? ? [account setName:@"Savings"];
?? ? // 使用setValue: forKey:沟绪。
?? ? [account setValue:@"Savings" forKey:@"name"];
?? ? // 使用密鑰路徑刮便,其中‘a(chǎn)ccount’是‘document’的kvc兼容屬性。
?? ? [document setValue:@"Savings" forKeyPath:@"account.name"];
?? ? //使用mutableArrayValueForKey:檢索關(guān)系代理對象绽慈。
?? ? Transaction *newTransaction = <#Create a new transaction for the account#><#為帳戶#>創(chuàng)建一個新事務(wù);
?? ? NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
?? ? [transactions addObject:newTransaction];
?? ? 手動更改通知
?? ? 在某些情況下恨旱,您可能希望控制通知流程抄肖,例如,最小化由于應(yīng)用程序特定原因而不必要的觸發(fā)通知窖杀,或者將一些更改分組到單個通知中漓摩。手動更改通知提供了這樣做的方法。
?? ? 手動通知和自動通知并不相互排斥入客。除了已經(jīng)存在的自動通知之外管毙,您還可以自由地發(fā)出手動通知。更典型的是桌硫,您可能希望完全控制特定屬性的通知夭咬。在本例中,您覆蓋了automaticallyNotifiesObserversForKey:的NSObject實現(xiàn)铆隘。對于您希望阻止其自動通知的屬性卓舵,automaticallyNotifiesObserversForKey:的子類實現(xiàn)應(yīng)該返回NO。子類實現(xiàn)應(yīng)該為任何未識別的鍵調(diào)用super膀钠。清單2中的示例支持balance屬性的手動通知掏湾,允許超類確定所有其他鍵的通知。
?? ? 例二automaticallyNotifiesObserversForKey:
?? ? + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
?? ? BOOL automatic = NO;
?? ? if ([theKey isEqualToString:@"balance"]) {
?? ? automatic = NO;
?? ? }
?? ? else {
?? ? automatic = [super automaticallyNotifiesObserversForKey:theKey];
?? ? }
?? ? return automatic;
?? ? }
?? ? 要實現(xiàn)手動觀察者通知肿嘲,您可以在更改值之前調(diào)用willChangeValueForKey:融击,在更改值之后調(diào)用didChangeValueForKey:。清單3中的示例實現(xiàn)了balance屬性的手動通知雳窟。
?? ? 清單3實現(xiàn)手動通知的示例訪問器方法
?? ? - (void)setBalance:(double)theBalance {
?? ? [self willChangeValueForKey:@"balance"];
?? ? _balance = theBalance;
?? ? [self didChangeValueForKey:@"balance"];
?? ? }
?? ? 您可以通過首先檢查值是否已更改來最小化發(fā)送不必要的通知尊浪。清單4中的示例測試balance的值,并且只在余額發(fā)生更改時提供通知封救。
?? ? 清單4在提供通知之前測試更改的值
?? ? - (void)setBalance:(double)theBalance {
?? ? if (theBalance != _balance) {
?? ? [self willChangeValueForKey:@"balance"];
?? ? _balance = theBalance;
?? ? [self didChangeValueForKey:@"balance"];
?? ? }
?? ? }
?? ? 如果單個操作導(dǎo)致多個鍵發(fā)生更改拇涤,則必須嵌套更改通知,如清單5所示誉结。
?? ? 清單5嵌套多個鍵的更改通知
?? ? - (void)setBalance:(double)theBalance {
?? ? [self willChangeValueForKey:@"balance"];
?? ? [self willChangeValueForKey:@"itemChanged"];
?? ? _balance = theBalance;
?? ? _itemChanged = _itemChanged+1;
?? ? [self didChangeValueForKey:@"itemChanged"];
?? ? [self didChangeValueForKey:@"balance"];
?? ? }
?? ? 對于有序到多關(guān)系鹅士,不僅必須指定更改的鍵,還必須指定更改的類型和涉及的對象的索引搓彻。更改的類型是NSKeyValueChange如绸,它指定nskeyvaluechangeinsert、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement旭贬。受影響對象的索引作為NSIndexSet對象傳遞怔接。
?? ? 清單6中的代碼片段演示了如何包裝to-many關(guān)系事務(wù)中刪除的對象。
?? ? 清單6在一對多關(guān)系中實現(xiàn)手動觀察者通知
?? ? - (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
?? ? [self willChange:NSKeyValueChangeRemoval
?? ? valuesAtIndexes:indexes forKey:@"transactions"];
?? ? //刪除指定索引處的事務(wù)對象稀轨。
?? ? [self didChange:NSKeyValueChangeRemoval
?? ? valuesAtIndexes:indexes forKey:@"transactions"];
?? ? }
?? ? 注冊相關(guān)的鍵
?? ? 在許多情況下扼脐,一個屬性的值取決于另一個對象中的一個或多個其他屬性的值。如果一個屬性的值發(fā)生了變化,那么派生屬性的值也應(yīng)該標記以供更改瓦侮。如何確保為這些依賴屬性發(fā)布觀察通知的鍵值取決于關(guān)系的基數(shù)艰赞。
?? ? 一個關(guān)系
?? ? 要自動觸發(fā)一對一關(guān)系的通知,您應(yīng)該覆蓋keyPathsForValuesAffectingValueForKey:或者實現(xiàn)一個合適的方法肚吏,該方法遵循它為注冊依賴鍵定義的模式方妖。
?? ? 例如,一個人的全名取決于他的姓和名罚攀。返回全名的方法可以這樣寫:
?? ? - (NSString *)fullName {
?? ? return [NSString stringWithFormat:@"%@ %@",firstName, lastName];
?? ? }
?? ? 觀察fullName屬性的應(yīng)用程序必須在firstName或lastName屬性更改時得到通知党觅,因為它們會影響屬性的值。
?? ? 一種解決方案是覆蓋keyPathsForValuesAffectingValueForKey:指定person的fullName屬性依賴于lastName和firstName屬性斋泄。清單1顯示了這種依賴關(guān)系的示例實現(xiàn):
?? ? 清單1 keyPathsForValuesAffectingValueForKey的示例實現(xiàn):
?? ? + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
?? ? NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
?? ? if ([key isEqualToString:@"fullName"]) {
?? ? NSArray *affectingKeys = @[@"lastName", @"firstName"];
?? ? keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
?? ? }
?? ? return keyPaths;
?? ? }
?? ? 您的覆蓋通常應(yīng)該調(diào)用super并返回一個集合杯瞻,該集合中包含由此而產(chǎn)生的任何成員(以便不干擾超類中該方法的覆蓋)。
?? ? 您還可以通過實現(xiàn)一個類方法來實現(xiàn)相同的結(jié)果炫掐,該類方法遵循影響的keypathsforvaluesnaming約定魁莉,其中是依賴于值的屬性的名稱(第一個大寫字母)。使用此模式募胃,可以將清單1中的代碼重寫為名為keyPathsForValuesAffectingFullName的類方法旗唁,如清單2所示。
?? ? 清單2影響命名約定的keypathsforvaluesaffect的示例實現(xiàn)
?? ? + (NSSet *)keyPathsForValuesAffectingFullName {
?? ? return [NSSet setWithObjects:@"lastName", @"firstName", nil];
?? ? }
?? ? 當您使用類別向現(xiàn)有類添加計算屬性時摔认,您不能覆蓋keyPathsForValuesAffectingValueForKey:方法逆皮,因為您不應(yīng)該覆蓋類別中的方法。在這種情況下参袱,實現(xiàn)一個匹配的keypathsforvaluesaffect 類方法,以利用這種機制秽梅。
?? ? 注意:您不能通過實現(xiàn)keyPathsForValuesAffectingValueForKey:來設(shè)置對多個關(guān)系的依賴關(guān)系抹蚀。相反,您必須觀察to-many集合中每個對象的適當屬性企垦,并通過自己更新依賴鍵來響應(yīng)其值的更改环壤。
?? ? 很多關(guān)系
?? ? 方法不支持包含對多關(guān)系的鍵路徑。例如钞诡,假設(shè)您有一個部門對象郑现,該對象與一個雇員(雇員)具有多對多的關(guān)系,并且雇員具有一個salary屬性荧降。您可能希望Department對象具有totalSalary屬性接箫,該屬性依賴于關(guān)系中所有員工的工資。例如朵诫,keypathsforvaluesaffect total salary和return employees就不能這樣做辛友。薪水是關(guān)鍵。
?? ? 兩種情況都有兩種可能的解決方案:
?? ? 您可以使用鍵值觀察來將父節(jié)點(在本例中為Department)注冊為所有子節(jié)點(在本例中為employee)的相關(guān)屬性的觀察者剪返。您必須以觀察者的身份添加和刪除父對象废累,因為在關(guān)系中添加和刪除子對象邓梅。
?? ? - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
?? ? if (context == totalSalaryContext) {
?? ? [self updateTotalSalary];
?? ? }
?? ? else
?? ? //處理其他觀察和/或調(diào)用super…
?? ? }
?? ? - (void)updateTotalSalary {
?? ? [self setTotalSalary:[self valueForKeyPath:@"employees.@sum.salary"]];
?? ? }
?? ? - (void)setTotalSalary:(NSNumber *)newTotalSalary {
?? ? if (totalSalary != newTotalSalary) {
?? ? [self willChangeValueForKey:@"totalSalary"];
?? ? _totalSalary = newTotalSalary;
?? ? [self didChangeValueForKey:@"totalSalary"];
?? ? }
?? ? }
?? ? - (NSNumber *)totalSalary {
?? ? return _totalSalary;
?? ? }
?? ? 如果使用Core Data,可以將父對象注冊到應(yīng)用程序的通知中心邑滨,作為其托管對象上下文的觀察者日缨。父節(jié)點應(yīng)該以類似于觀察鍵值的方式響應(yīng)子節(jié)點發(fā)布的相關(guān)更改通知。
?? ? 觀察實現(xiàn)細節(jié)的鍵值
?? ? 自動鍵值觀察是使用isa- swizzle技術(shù)實現(xiàn)的掖看。
?? ? 顧名思義匣距,isa指針指向維護分派表的對象的類。這個分派表本質(zhì)上包含指向類實現(xiàn)的方法和其他數(shù)據(jù)的指針乙各。
?? ? 當一個觀察者注冊了一個對象的屬性時墨礁,被觀察對象的isa指針被修改,指向一個中間類而不是真正的類耳峦。因此恩静,isa指針的值不一定反映實例的實際類。
?? ? 永遠不要依賴isa指針來確定類的成員蹲坷。相反驶乾,您應(yīng)該使用類方法來確定對象實例的類