KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
基本使用方法
//
// WKPerson.h
// lllmmnn
//
// Created by wukai on 2019/1/4.
// Copyright © 2019年 诸葛小亮. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface WKPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@end
//
// WKPerson.m
// lllmmnn
//
// Created by wukai on 2019/1/4.
// Copyright © 2019年 诸葛小亮. All rights reserved.
//
#import "WKPerson.h"
@implementation WKPerson
- (void)setAge:(NSInteger)age{
_age = age;
}
@end
self.person1 = [[WKPerson alloc] init];
self.person1.age = 1;
//content 代表文本 现在监听
[person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
person1.age = 10;
//建值更改后 会自动进入 conten传入123 这里的content就是123
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
//移除
- (void)dealloc{
[self.person removeObserver:self forKeyPath:@"age"];
}
- 为啥更改了键值之后就可以立马收到通知
- 我们来深入的看看
#import "ViewController.h"
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person1.age = 10;
NSLog(@"更改之后");
}
@end
#import "WKPerson.h"
@implementation WKPerson
- (void)setAge:(NSInteger)age{
_age = age;
}
@end
2019-01-05 11:43:36.779705+0800 lllmmnn[3027:195001] {
kind = 1;
new = 10;
old = 1;
}
2019-01-05 11:46:38.422867+0800 lllmmnn[3027:195001] 更改之后
结论是 执行完的结果是 set里面之后,立即通知发生更改,我们来看看更改了什么
self.person1 = [[WKPerson alloc] init];
self.person1.age = 1;
self.person2 = [[WKPerson alloc] init];
self.person2.age = 2;
NSLog(@"KVO之前 person1 = %@",object_getClass(_person1));
NSLog(@"KVO之前 person2 = %@",object_getClass(_person2) );
[_person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
NSLog(@"KVO之后 person1 = %@",object_getClass(_person1));
NSLog(@"KVO之后 person2 = %@",object_getClass(_person2));
NSLog(@"KVO之后的父类 person2 = %@",[_person2 superclass]);
NSLog(@"KVO之后的父类 person1 = %@",[object_getClass(_person1) superclass]);
2019-01-05 11:43:24.794017+0800 lllmmnn[3027:195001] KVO之前 person1 = WKPerson
2019-01-05 11:43:24.794119+0800 lllmmnn[3027:195001] KVO之前 person2 = WKPerson
2019-01-05 11:43:33.617426+0800 lllmmnn[3027:195001] KVO之后 person1 = NSKVONotifying_WKPerson
2019-01-05 11:43:34.346046+0800 lllmmnn[3027:195001] KVO之后 person2 = WKPerson
2019-01-05 11:54:37.120260+0800 lllmmnn[3181:214328] KVO之后的父类 person2 = NSObject
2019-01-05 11:54:37.120365+0800 lllmmnn[3181:214328] KVO之后的父类 person1 = WKPerson
由此可以说明 当KVO键值监听之后,系统会通过runtime生成一个衍生类NSKVONotifying_WKPerson 继承于WKPerson
接下来我们来看看 setAge里面的实现
NSLog(@"KVO之前person1=%p",[_person1 methodForSelector:@selector(setAge:)]);
NSLog(@"KVO之前person2=%p",[_person2 methodForSelector:@selector(setAge:)]);
[_person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
NSLog(@"KVO之后person1=%p",[_person1 methodForSelector:@selector(setAge:)]);
NSLog(@"KVO之后person2=%p",[_person2 methodForSelector:@selector(setAge:)]);
2019-01-05 13:52:52.379255+0800 lllmmnn[4611:339139] KVO之前person1=0x101dcc430
2019-01-05 13:52:52.379450+0800 lllmmnn[4611:339139] KVO之前person2=0x101dcc430
2019-01-05 13:52:54.873704+0800 lllmmnn[4611:339139] KVO之后person1=0x102112bf4
2019-01-05 13:52:54.873833+0800 lllmmnn[4611:339139] KVO之后person2=0x101dcc430
我们发现KVO之后person1的内存地址发生了变化
(lldb) p (IMP)0x101dcc430
(IMP) $0 = 0x0000000101dcc430 (lllmmnn`-[WKPerson setAge:] at WKPerson.m:13)
(lldb) p (IMP)0x102112bf4
(IMP) $1 = 0x0000000102112bf4 (Foundation`_NSSetLongLongValueAndNotify)
- LLDB调试之后我们发现没有KVO的setAge方法 内部实现就是[WKPerson setAge:]
- 调用了KVO的 setAge内部实现是调用了Foundation框架里面的 _NSSetLongLongValueAndNotify 这个方法
- 由此我们可以得出 KVO 是在setAge方法里面调用了_NSSetLongLongValueAndNotify这个方法
#pragma mark - 获取类的所有方法
- (NSArray *)getClassMethodsWithClass:(Class)cls {
NSMutableArray *mutArr = [NSMutableArray array];
unsigned int outCount;
/** 第一个参数:要获取哪个类的方法
* 第二个参数:获取到该类的方法的数量
*/
Method *methodList = class_copyMethodList(cls, &outCount);
// 遍历所有的方法
for (int i = 0; i<outCount; i++) {
SEL name = method_getName(methodList[i]);
[mutArr addObject:NSStringFromSelector(name)];
}
return [NSArray arrayWithArray:mutArr];
}
NSArray *arry = [self getClassMethodsWithClass:object_getClass(_person1)];
NSLog(@"%@",arry);
2019-01-05 13:58:24.512361+0800 lllmmnn[4611:339139] (
"setAge:",
class,
dealloc,
"_isKVOA"
)