??KVO簡介:鍵值觀察药磺,是基于鍵值編碼(KVC)的一種觀察模式。 是iOS中常用的一種消息傳遞機制煤伟。
??對接模式:發(fā)送者和接收者的關(guān)系為一對多的關(guān)系癌佩,一個發(fā)送者可以有多個接收者。
??使用鍵值觀察需要被觀察對象編寫符合KVC標(biāo)準(zhǔn)的存取方法,使用步驟分為以下三步:
- 第一步:注冊成為KVO觀察者
- 第二步:定義KVO的回調(diào)
- 第三步:移除KVO觀察者
??第一步:調(diào)用注冊方法成為觀察者
[ObjectOfObserved addObserver:observerObject forKeyPath:@"example" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
KVO注冊方法參數(shù):
- ObjectOfObserved:NSObject類型,為被觀察者對象
- 參數(shù)Observer:NSObject類型沈矿,為觀察者對象秒啦,即要注冊成為觀察者的對象(既然是在觀察者的.m文件中實現(xiàn)的,那么該參數(shù)則為self)
- 參數(shù)KeyPath:NSString類型,鍵路徑參數(shù),描述將要觀察的屬性,即被觀察者的屬性參數(shù)桥胞。(如:被觀察者有一個命名為example的屬性,而且你要觀察的正是這個屬性考婴,那就設(shè)置這個參數(shù)為@"example")
- 參數(shù)options:枚舉類型贩虾,有四個選項,標(biāo)識KVO希望變化如何傳遞給觀察者沥阱,可以使用"|"進(jìn)行多選(這里觀察屬性變化的新值和舊值)
options枚舉:
1缎罢、NSKeyValueObservingOptionNew:該枚舉值保證KVO監(jiān)聽屬性的新內(nèi)容,當(dāng)屬性內(nèi)容變化時考杉,KVO會記錄屬性的新內(nèi)容策精,這樣KVO回調(diào)方法中的參數(shù)change字典中包括改變后的值,可以通過KVO的監(jiān)聽回調(diào)方法獲取到屬性的新內(nèi)容
2崇棠、NSKeyValueObservingOptionOld:該枚舉值保證KVO監(jiān)聽屬性的原內(nèi)容咽袜,當(dāng)屬性內(nèi)容變化時,KVO會記錄屬性的原內(nèi)容枕稀,這樣KVO回調(diào)方法中的參數(shù)change字典包括改變前的值询刹,可以通過KVO的監(jiān)聽回調(diào)方法獲取到屬性的原內(nèi)容
3谜嫉、NSKeyValueObservingOptionInitial: 注冊后立刻觸發(fā)KVO通知,添加該枚舉后凹联,即使屬性值沒有變化沐兰,也會調(diào)用KVO回調(diào)方法
4、NSKeyValueObservingOptionPrior:值改變前是否也要通知(這個key決定了是否在改變前改變后通知兩次)
- 參數(shù)context:void類型指針蔽挠,上下文內(nèi)存區(qū)住闯,通常設(shè)置為nil 。這個會傳遞到觀察者的函數(shù)中澳淑,用來區(qū)分不同的監(jiān)聽消息比原。
如何設(shè)置context:
可以參考這篇文章Key-Value Observing中《正確的上下文聲明》部分;以及這篇文章 kvo/gcd中關(guān)于context指針的賦值
??第二步:在觀察者的.m實現(xiàn)文件中定義KVO的回調(diào)方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
}
該方法是系統(tǒng)提供的觀察者實現(xiàn)鍵值觀察方法,作為觀察者,需要實現(xiàn)這個方法杠巡。當(dāng)被觀察的被觀察屬性發(fā)生變化時(這里的變化是指賦值操作量窘,只要給屬性賦值就會調(diào)用,就算賦相同的內(nèi)容忽孽,也會調(diào)用)绑改,會自動調(diào)用這個方法谢床。
KVO回調(diào)方法中參數(shù):
- 參數(shù)keyPath:NSString類型兄一,被監(jiān)聽的keyPath , 用來區(qū)分不同的KVO監(jiān)聽
- 參數(shù)object:id類型,為被監(jiān)察的對象
- 參數(shù)change:NSDictionary類型识腿,保存被監(jiān)察者信息的字典
- 參數(shù)context:void類型出革,上下文,用來區(qū)分不同的KVO監(jiān)聽
KVO回調(diào)方法操作:
- 當(dāng)監(jiān)聽者監(jiān)聽了多個被監(jiān)聽者時渡讼,可以在回調(diào)方法中通過參數(shù)keyPath來判斷是哪個監(jiān)聽消息骂束,```
[keyPath isEqualToString:@"name"]
* 獲取被監(jiān)聽者對象```
監(jiān)聽者對象類型 *變量名 = object;
- 獲取被監(jiān)聽者內(nèi)容未變時的屬性對象```
屬性對象類型 * old = [change objectForKey:NSKeyValueChangeOldKey];
* 獲取被監(jiān)聽者內(nèi)容已變時的屬性對象```
屬性對象類型 * new = [change objectForKey:NSKeyValueChangeNewKey];
??第三步:在觀察者的.m文件中的dealloc方法中移除觀察者
- (void)dealloc {
// 調(diào)用移除觀察者的方法:- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
[ObjectOfObserved removeObserver:Observer forKeyPath:@"example"];
}
KVO移除方法參數(shù):
- ObjectOfObserved:NSObject類型成箫,為被觀察者對象
- 參數(shù)Observer:NSObject類型展箱,為觀察者對象,即要注冊成為觀察者的對象(既然是在觀察者的.m文件中實現(xiàn)的蹬昌,那么該參數(shù)則為self)
- 參數(shù)KeyPath:NSString類型混驰,鍵路徑參數(shù),即被觀察者的屬性參數(shù)皂贩。(如:被觀察者有一個命名為example的屬性栖榨,而且你要觀察的正是這個屬性,那就設(shè)置這個參數(shù)為@"example")
注:如果忘記解除移除監(jiān)察明刷,會導(dǎo)致資源泄露
??拓展一: KVO手動設(shè)置
?KVO模型中婴栽,有兩種通知觀察者的方式,自動通知和手動通知辈末。一般我們都用系統(tǒng)自動通知愚争,文章開篇介紹的就是KVO自動通知映皆。
?KVO手動通知的設(shè)置出了完成自動通知的三個步驟,需要在被觀察者的.m實現(xiàn)文件中重寫類方法:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
}
返回NO時准脂,告訴系統(tǒng)使用KVO手動通知劫扒;返回YES時,使用KVO自動通知狸膏,默認(rèn)為YES;
參數(shù)key為NSString類型沟饥,被監(jiān)聽的keyPath , 用來區(qū)分不同的KVO監(jiān)聽。
另外湾戳,在當(dāng)被監(jiān)聽的屬性發(fā)生變化時贤旷,需要調(diào)用屬性值將要改變的方法和屬性值已經(jīng)改變方法
// 手動通知在需要改變example變量的地方,使用
[self willChangeValueForKey:@"example"];
self.example = @"我是被監(jiān)聽的屬性砾脑,字符串類型的";
[self didChangeValueForKey:@"example"];
自動通知在需要改變example變量的地方幼驶,使用KVC設(shè)置屬性內(nèi)容的方法,而不是僅僅使用簡單賦值韧衣。
[self setValue:@"我是被監(jiān)聽的屬性盅藻,字符串類型的" forKey:@"example"];
手動改變屬性內(nèi)容時,我們需要在3個地方改變屬性的內(nèi)容值畅铭,即:請求結(jié)束時氏淑、連接出錯誤,線程被cancel硕噩。請在對應(yīng)的方法代碼中加入手動變值的語句假残。
??拓展二: KVC簡單介紹
可以參看這篇文章:iOS開發(fā)技巧系列---詳解KVC(我告訴你KVC的一切)
參考文章