基本原理-> 给一个对象的属性添加监听 当属性值发生变化时 会触发监听器的监听的方法
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *p;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.p=[[Person alloc]init];
self.p.age=5;
[self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
// Do any additional setup after loading the view.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p.age=10;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"\n%@\n%@\n%@\n%@",keyPath,object,change,context);
/*
age
<Person: 0x600001390970>
{
kind = 1;
new = 10;
old = 5;
}
额外信息
*/
}
-(void)dealloc{
[self.p removeObserver:self forKeyPath:@"age"];
}
2、在给person 添加监听之后、其实苹果底层通过运行时动态给person添加了一个子类 NSKVONotifying_Person
NSLog(@"%s",object_getClassName(self.p)); // Person
[self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
NSLog(@"%s",object_getClassName(self.p)); //NSKVONotifying_Person
3、在新生产的类会新生成4个方法
- (void)viewDidLoad {
[super viewDidLoad];
self.p=[[Person alloc]init];
self.p.age=5;
[self printMethodNamesOfClass:object_getClass(self.p)]; // Person age, setAge:
[self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
[self printMethodNamesOfClass:object_getClass(self.p)];//NSKVONotifying_Person setAge:, class, dealloc, _isKVOA,
}
- (void)printMethodNamesOfClass:(Class)cls
{
unsigned int count;
// 获得方法数组
Method *methodList = class_copyMethodList(cls, &count);
// 存储方法名
NSMutableString *methodNames = [NSMutableString string];
// 遍历所有的方法
for (int i = 0; i < count; i++) {
// 获得方法
Method method = methodList[i];
// 获得方法名
NSString *methodName = NSStringFromSelector(method_getName(method));
// 拼接方法名
[methodNames appendString:methodName];
[methodNames appendString:@", "];
}
// 释放
free(methodList);
// 打印方法名
NSLog(@"%@ %@", cls, methodNames);
}
4个方法介绍
- (void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在
- (Class)class
{
return [MJPerson class];
}
- (void)dealloc
{
// 收尾工作
}
- (BOOL)_isKVOA
{
return YES;
}
4、在新生成的这个类中 修改对象的属性时-> 其实会类似重写Set方法- 会调用Foundation 框架中的
_NSSetXXXValueAndNotify 函数:
- willChangeValueForKey:
- 父类原来的setter
- didChangeValueForKey:
- 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
5、如要要手动触发一个KVO 监听器的监听方法
- 调用改对象的 willChangeValueForKey
- 调用改对象的 didChangeValueForKey
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// self.p.age=10;
[self.p willChangeValueForKey:@"age"];
[self.p didChangeValueForKey:@"age"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"\n%@\n%@\n%@\n%@",keyPath,object,change,context);
/*
age
<Person: 0x6000019947f0>
{
kind = 1;
new = 5;
old = 5;
}
额外信息
*/
}
6、修改对象的成员变量是不会触发KVO的、KVC赋值是能够触发KVO
来源:https://www.cnblogs.com/ZhangShengjie/p/12094434.html