??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希望變化如何傳遞給觀察者,可以使用"|"進行多選(這里觀察屬性變化的新值和舊值)
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的一切)
參考文章