? ? ? ? ? KVO在OC中是實現鍵值(key-value-observing)觀察的方式,在設計模式中是典型的觀察者模式, 當觀察者將被觀察者的某個屬性設置為觀察的對象時,若被觀察的該屬性值發生變化時,就會觸發觀察者對象所實現的KVO接口方法,從而達到通知觀察者的目的。
? ? ? ? 簡單來說KVO可以通過監聽key,來獲得value的變化,用來在對象之間監聽狀態變化。KVO的定義都是對NSObject的擴展來實現的,Objective-C中有個顯式的NSKeyValueObserving類別名,所以對于所有繼承了NSObject的類型,都能使用KVO。
? ? ? ? ?KVO的實現是基于iOS runtime機制的isa-swizzling(指針替換),當一個對象的屬性被注冊成被觀察對象時,會生成一個中間類繼承自該類,然后將該類的isa指針指向新生成的子類,這樣被觀察的對象就變成了這個中間類,同時重寫了被觀察屬性的setter方法,當新對象的屬性發生變化時,則會依次通知注冊的觀察者對象。
自動KVO(默認方式)
a、添加觀測者
-(void)addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context;
b、當觀測的屬性值發生改變時調用的函數
-(void)observeValueForKeyPath:(nullable NSString*)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey,id>*)change context:(nullablevoid*)context;
c、移除觀察者
- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath context:(nullablevoid*)context;
? ? ? ? 注意,如果被觀察的屬性沒有調用Setter方法,而是通過直接訪問其實例變量(_下劃線方法)是不會觸發回調的。
手動KVO
? ? ? ? KVO在屬性發生改變的時候默認是自動調用的,如果需要手動的控制這個調用時機,那么在被觀察的對象.m文件中調用如下方法:
+(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key{
returnYES;//默認,自動模式
returnNO;//手動模式
}
同時在屬性變化之前,調用:
-(void)willChangeValueForKey:(NSString*)key;
在屬性變化之后,調用:
-(void)didChangeValueForKey:(NSString*)key;
? ? ? ? 其實無論屬性的值是否發生改變,是否調用Setter方法,只要調用了willChangeValueForKey:和didChangeValueForKey:就會觸發回調。
依賴鍵KVO
如果在當前Person類中引入另外一個Dog類:
@interface Dog : NSObject
@property (copy, nonatomic) NSString *age;
@property (copy, nonatomic) NSString *name;
@end
那么此時我們怎么通過Person來觀察Dog類的age屬性呢?
[_p addObserver:self forKeyPath:@"dog.age" options:NSKeyValueObservingOptionNew ? ?context:nil];
我們現在希望,只要Dog類中有屬性的變化,就會通知到Person類,如果我們每一個屬性都添加一遍觀察者會很麻煩,這里就需要用到屬性依賴。我們在Person類的.m中添加一個方法:
+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
? ? NSSet *keyPath = [super keyPathsForValuesAffectingValueForKey:key];
? ? if([keyisEqual:@"dog"]) {
? ? ? ? keyPath = [[NSSetalloc]initWithObjects:@"_dog.age",@"_dog.name",nil];
? ? }
? ? returnkeyPath;
}
同時在添加觀察者時,不用對dog具體的屬性添加,直接對dog進行觀察。
[_p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
? ? ? ? 設置鍵之間的依賴關系+(NSSet<NSString*>*)keyPathsForValuesAffectingValueForKey:(NSString*)key;當添加一個被觀察的keyPath,這個方法就會走。其大概意思是就是:用一組鍵的值去影響另一個鍵值。注意該方法一定要考慮非指定key時的情況要調用[super keyPathsForValuesAffectingValueForKey:key],不能影響其他的情況。