KVO簡介
KVO<NSKeyValueObserving>昂灵,是一個非正式協(xié)議宗收,提供了一個途徑硼端,使對象(觀察者)能夠觀察其他對象(被觀察者)的屬性,當(dāng)被觀察者的屬性發(fā)生變化時醋安,觀察者就會被告知該變化。由NSKeyValueObserving協(xié)議提供支持墓毒,NSObject類繼承了該協(xié)議吓揪,所以NSObject的子類都可使用該方法。
基本方法
添加觀察者
/*
object : 被觀察對象
observer: 觀察對象
forKeyPath里面帶上property的name所计,如UIView的frame柠辞、center,如自定義對象的成員變量等等
options: 有4個值主胧,分別是:
NSKeyValueObservingOptionNew 把更改之后的值提供給處理方法
NSKeyValueObservingOptionOld 把更改之前的值提供給處理方法
NSKeyValueObservingOptionInitial 把初始化的值提供給處理方法叭首,一旦注冊习勤,立馬就會調(diào)用一次。通常它會帶有新值焙格,而不會帶有舊值图毕。
NSKeyValueObservingOptionPrior 分2次調(diào)用。在值改變之前和值改變之后眷唉。
context: 可以帶入一些參數(shù)予颤,其實這個挺好用的,任何類型都可以冬阳,自己強(qiáng)轉(zhuǎn)就好了
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
實現(xiàn)觀察者回調(diào)方法
/*
keyPath: 對應(yīng)forKeyPath
object: 被觀察的對象
change: 對應(yīng)options里的NSKeyValueObservingOptionNew蛤虐、NSKeyValueObservingOptionOld等
context: 對應(yīng)context
*/
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary*)change context:(nullable void *)context
移除觀察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
KVO模式
KVO分為自動和手動兩種模式
簡單的實現(xiàn)一個自動模式
//自動模式
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"A";
//NSLog(@"class:%s",object_getClassName(person));
NSLog(@"before:%@",[self findSubClass:[person class]]);
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"before:%@",[self findSubClass:[person class]]);
//NSLog(@"class:%s",object_getClassName(person));
person.name = @"B";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@",change);
}
/*
利用runtime獲取該類的所有子類
*/
- (NSArray *)findSubClass:(Class)defaultClass {
int count = objc_getClassList(NULL,0);
if (count <= 0) {
return [NSArray array];
}
NSMutableArray *output = [NSMutableArray arrayWithObject:defaultClass];
Class *classes = (Class *)malloc(sizeof(Class) * count);
objc_getClassList(classes, count);
for (int i = 0; i < count; i++) {
if (defaultClass == class_getSuperclass(classes[i])) {
[output addObject:classes[i]];
}
}
return output;
}
image.png
實現(xiàn)一個手動模式,首先要明白KVO自動模式的實現(xiàn)原理:
如果原類為 Person肝陪,那么會自動生成一個繼承Person類的派生類笆焰,名為 NSKVONotifying_Person (見自動模式截圖),系統(tǒng)會偷偷將原類Person的isa 指針指向動態(tài)生成的派生類见坑,在給被監(jiān)控屬性賦值時執(zhí)行的其實是派生類的 setter 方法嚷掠,該派生類的setter方法中會增加willChangeValueForKey和didChangeValueForKey方法。
//手動模式
/*
需要在被觀察者對象(這里被觀察者是Pesron類)中重寫automaticallyNotifiesObserversForKey靜態(tài)方法
在這里荞驴,我僅針對成員變量name進(jìn)行手動
*/
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
return NO;
}
return YES;
}
/*
手動模式就需要手動調(diào)用willChangeValueForKey和didChangeValueForKey兩個方法
*/
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"A";
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
person.name = @"B";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@",change);
NSLog(@"class:%@",[object class]);
}