KVO
KVO 即 Key-Value Observing汞窗,翻譯成鍵值觀察。它是一種觀察者模式的衍生刊苍。其基本思想是既们,對(duì)目標(biāo)對(duì)象的某屬性添加觀察,當(dāng)該屬性發(fā)生變化時(shí)正什,通過觸發(fā)觀察者對(duì)象實(shí)現(xiàn)的KVO接口方法啥纸,來自動(dòng)的通知觀察者。
觀察者模式是什么 一個(gè)目標(biāo)對(duì)象管理所有依賴于它的觀察者對(duì)象婴氮,并在它自身的狀態(tài)改變時(shí)主動(dòng)通知觀察者對(duì)象斯棒。這個(gè)主動(dòng)通知通常是通過調(diào)用各觀察者對(duì)象所提供的接口方法來實(shí)現(xiàn)的。
簡(jiǎn)單來說KVO可以通過監(jiān)聽key莹妒,來獲得value的變化名船,用來在對(duì)象之間監(jiān)聽狀態(tài)變化。
KVO使用
注冊(cè)與解除注冊(cè)
如果我們已經(jīng)有了包含可供鍵值觀察屬性的類旨怠,那么就可以通過在該類的對(duì)象(被觀察對(duì)象)上調(diào)用以下兩個(gè)方法來注冊(cè)和解除注冊(cè)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
observer: 被觀察者
keyPath:鍵路徑參數(shù)渠驼,描述將要觀察的屬性,相對(duì)于被觀察者
options:標(biāo)識(shí)KVO希望變化如何傳遞給觀察者鉴腻,可以使用|進(jìn)行多選(有四個(gè)選項(xiàng))
context:上下文內(nèi)存區(qū)迷扇,通常為nil
options所包括的內(nèi)容
NSKeyValueObservingOptionNew:change字典包括改變后的值
NSKeyValueObservingOptionOld:change字典包括改變前的值
NSKeyValueObservingOptionInitial:注冊(cè)后立刻觸發(fā)KVO通知
NSKeyValueObservingOptionPrior:值改變前是否也要通知(這個(gè)key決定了是否在改變前改變后通知兩次)
處理變更通知
每當(dāng)監(jiān)聽的keyPath發(fā)生變化了,就會(huì)在這個(gè)函數(shù)中回調(diào)爽哎。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
在這里蜓席,change 這個(gè)字典保存了變更信息,具體是哪些信息取決于注冊(cè)時(shí)的 NSKeyValueObservingOptions课锌。
手動(dòng)KVO(禁用KVO)
KVO中厨内,當(dāng)被觀察的屬性改變時(shí),KVO被觸發(fā)渺贤。舉例如下:
KVO監(jiān)測(cè)Person類實(shí)例person的name屬性雏胃。當(dāng)name值改變時(shí),方法- setName:被調(diào)用志鞍。此時(shí)下面兩個(gè)方法會(huì)在運(yùn)行- setName:之前之后被調(diào)用瞭亮。
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
但是有時(shí)候我們并不是都需要每次改變屬性值的前后都調(diào)用這兩個(gè)方法,那么我們這時(shí)候就需要手動(dòng)KVO,先把這兩個(gè)方法的自動(dòng)調(diào)用給禁用,然后我們?cè)僮约菏謩?dòng)去實(shí)現(xiàn)KVO的通知固棚。代碼如下:
+ (BOOL)automaticallyNotifiesObserversForName{ //禁用KVO的自動(dòng)通知
return NO;
}
- (void)setName:(NSString *)lComponent{
if (_name == name) {
return;
}
[self willChangeValueForKey:@"name"];
_name == name;
[self didChangeValueForKey:@"name"];}
上述代碼的automaticallyNotifiesObserversForName方法是選擇是否自動(dòng)通知统翩,我們只需返回NO仙蚜,系統(tǒng)就不會(huì)自動(dòng)通知,然后再在setName方法中我們手動(dòng)調(diào)用通知的兩個(gè)方法厂汗,甚至可以加上加上自己想要的判斷條件委粉。
針對(duì)非自動(dòng)通知的屬性,可以分別在變化之前和之后手動(dòng)調(diào)用如下方法(will在前面徽,did在后)來手動(dòng)通知觀察者:
(will/did)ChangeValueForKey:(will/did)ChangeValueForKey:withSetMutation:usingObjects:
(will/did)Change:valuesAtIndexes:forKey:
鍵值依賴(注冊(cè)從屬key)
有時(shí)候一個(gè)屬性的值依賴于另一對(duì)象中的一個(gè)或多個(gè)屬性艳丛,如果這些屬性中任一屬性的值發(fā)生變更,被依賴的屬性值也應(yīng)當(dāng)為其變更進(jìn)行標(biāo)記趟紊。因此,object 引入了依賴鍵碰酝。要為一對(duì)一關(guān)系自動(dòng)觸發(fā)通知霎匈,應(yīng)該重寫keyPathsForValuesAffectingValueForKey或?qū)崿F(xiàn)一個(gè)合適的方法,該方法遵循它為注冊(cè)依賴鍵定義的模式送爸。
例如铛嘱,fullName取決于firstName和lastName。返回fullName的方法可以寫成如下:
- (NSString *)fullName {
return [NSString stringWithFormat:@“%@%@”袭厂,firstName墨吓,lastName];
}
當(dāng)firstName或lastName發(fā)生改變時(shí),必須通知觀察fullName屬性的程序纹磺,因?yàn)樗鼈冇绊戇@個(gè)屬性的值帖烘。一個(gè)解決方案是重寫keyPathsForValuesAffectingValueForKey來指定fullName屬性依賴于lastName和firstName。
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"fullName"]) {
NSArray *affectingKeys = @[@"lastName", @"firstName"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
通過重寫keyPathsForValuesAffecting<Key>也可以達(dá)到相同的效果橄杨。
+ (NSSet *)keyPathsForValuesAffectingFullName {
return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}
KVO和線程
一個(gè)需要注意的地方是秘症,KVO 行為是同步的,并且發(fā)生與所觀察的值發(fā)生變化的同樣的線程上式矫。沒有隊(duì)列或者 Run-loop 的處理乡摹。手動(dòng)或者自動(dòng)調(diào)用 -didChange... 會(huì)觸發(fā) KVO 通知。
所以采转,當(dāng)我們?cè)噲D從其他線程改變屬性值的時(shí)候我們應(yīng)當(dāng)十分小心聪廉,除非能確定所有的觀察者都用線程安全的方法處理 KVO 通知。通常來說故慈,我們不推薦把 KVO 和多線程混起來板熊。如果我們要用多個(gè)隊(duì)列和線程,我們不應(yīng)該在它們互相之間用 KVO惯悠。
KVO 是同步運(yùn)行的這個(gè)特性非常強(qiáng)大邻邮,只要我們?cè)趩我痪€程上面運(yùn)行(比如主隊(duì)列 main queue),KVO 會(huì)保證下列兩種情況的發(fā)生:
首先克婶,如果我們調(diào)用一個(gè)支持 KVO 的 setter 方法筒严,如下所示:
self.exchangeRate = 2.345;
KVO 能保證所有 exchangeRate 的觀察者在 setter 方法返回前被通知到丹泉。
KVO原理
1.automaticallyNotifiesObserversForKey
為YES
時(shí)注冊(cè)觀察屬性會(huì)生成動(dòng)態(tài)子類NSKVONotifying_XXX
2.動(dòng)態(tài)子類觀察的是setter
方法
3.動(dòng)態(tài)子類重寫了觀察屬性的setter
方法、dealloc
鸭蛙、class
摹恨、_isKVOA
方法
- setter
方法用于觀察鍵值
- dealloc
方法用于釋放時(shí)對(duì)isa
指向進(jìn)行操作
- class
方法用于指回動(dòng)態(tài)子類的父類
- _isKVOA
用來標(biāo)識(shí)是否是在觀察者狀態(tài)的一個(gè)標(biāo)志位
4.dealloc
之后isa
指向元類
5.dealloc
之后動(dòng)態(tài)子類不會(huì)銷毀