KVO的基本原理大概是這樣的數組
當一個對象被觀察時, 系統會新建一個子類NSNotifying_A ,在子類中重寫了對象被觀察屬性的 set方法, 而且改變了該對象的 isa 指針的指向(指向了新建的子類) , 當屬性的值發生改變了, 會調用子類的set方法, 而後發出通知xss
一. KVO 的基本使用spa
給_person對象 添加觀察者self, 當person對象的name的值發生改變的時候, 會觸發observer方法指針
_person = [Person new]; p.name = @"oldName";
//添加觀察者
// [p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
[_person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
// 所觀察的對象的keyPath 發生改變的時候, 會觸發 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ NSLog(@"%@",keyPath); NSLog(@"%@",change); }
二. 當keyPath 爲對象時, 改對象有許多屬性, 怎麼辦?code
在person類中,重寫這個方法, 設置須要觀察的屬性 , 注意:"_dog.level"server
//返回一個容器, 裏面放的是NSString類型 + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; //觀察dog對象中的全部屬性 if ([key isEqualToString:@"dog"]) { keyPaths = [keyPaths setByAddingObjectsFromArray:@[@"_dog.level",@"_dog.age"]]; } return keyPaths; }
三. 手動觸發KVO對象
系統默認該對象的全部屬性 都能被觀察到 ,重寫下面方法, 能夠單獨設置某個屬性不能被觀察blog
//默認 yes, 默認自動觀察全部屬性 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{ return YES; }
//返回NO, 則不能被默認觀察到name + (BOOL)automaticallyNotifiesObserversOfName{ return NO; } + (BOOL)automaticallyNotifiesObserversOfAge{ return YES; }
當 name 發生改變的時, 手動觸發, 發送通知cmd
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //手動發通知 //即將改變(發一次通知) [_person willChangeValueForKey:@"name"]; _person.name = @"dddd"; //已經改變(發一次通知),一共發了兩次通知 [_person didChangeValueForKey:@"name"];
}
四. 自定義KVOstring
根據kvo的原理, 能夠自定義一個kvo, 建一個NSObject的分類, 添加方法
@interface NSObject (XSKVO) - (void)xs_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; @end
經過runtime的方式, 動態建立一個類, 並給該類添加方法
#import "NSObject+XSKVO.h" #import <objc/runtime.h> @implementation NSObject (XSKVO) - (void)xs_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{ //1.新建一個類 NSString *className = [@"XSKVO" stringByAppendingString: NSStringFromClass([self class])]; Class newClass = objc_allocateClassPair([self class], className.UTF8String, 0); //註冊類 objc_registerClassPair(newClass); //2.修改 調用者類型 object_setClass(self, newClass); //3.給子類添加set方法(子類裏面沒有set方法的) //OC方法:方法編號SEL ,方法實現IMP class_addMethod(newClass, @selector(setName:), xssetName, ""); } /* 隱藏的參數: self 方法的調用者 _cmd 方法的編號 */ void xssetName(id self,SEL _cmd, NSString *newName){ NSLog(@"自定義的實現%@",newName); //方案一:經過消息機制 發送消息 -observeValueForKeyPath } @end
五. 其餘
關於容器類(如:NSMutableArray)的觀察, 當經過addObject: 向數組中添加對象, 不會觸發KVO, 由於並無觸發set方法,
解決方法: 經過KVC 方法 - mutableArrayValueForKey: