- KVO key-Value Observing 键值监听 ,可以用于监听某个对象属性值的改变
使用了KVO监听的对象
-
runtime动态创建一个子类
image.png
KVO探究
#import "learnKVOdeepVC.h"
@interface Person : NSObject
@property(nonatomic,strong) NSString * name;
@end
@implementation Person
@end
@interface learnKVOdeepVC ()
@property(nonatomic,strong) Person * person1;
@property(nonatomic,strong) Person * person2;
@end
@implementation learnKVOdeepVC
- (void)viewDidLoad {
[super viewDidLoad];
//不设置颜色 touchesBegan 无效 这个需要注意
self.view.backgroundColor = [UIColor whiteColor];
self.person1 = [[Person alloc]init];
self.person1.name = @"person1";
self.person2 = [[Person alloc]init];
self.person2.name = @"person2";
NSLog(@"person1添加KVO监听之前 - %s %s", object_getClassName(self.person1),
object_getClassName(self.person2));
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld ;
[self.person1 addObserver:self forKeyPath:@"name" options:options context:@"123"];
NSLog(@"person1添加KVO监听之后 - %s %s", object_getClassName(self.person1),
object_getClassName(self.person2));
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.person1 setName:@"哈哈"];
}
-(void)dealloc{
[self.person1 removeObserver:self forKeyPath:@"name"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"监听到的%@的%@属性值改变了 - %@ - %@",object,keyPath,change,context);
}
@end
输出
2020-05-06 09:49:50.766225+0800 HYZExplore[4119:64877] person1添加KVO监听之前 - Person Person
2020-05-06 09:49:50.766508+0800 HYZExplore[4119:64877] person1添加KVO监听之后 - NSKVONotifying_Person Person
2020-05-06 09:49:51.825428+0800 HYZExplore[4119:64877] 监听到的<Person: 0x600002f5a1c0>的name属性值改变了 - {
kind = 1;
new = "\U54c8\U54c8";
old = person1;
} - 123
可见 person1的Class对象不再是Person 而是一个 NSKVONotifying_Person 的类对象
NSLog(@"person1添加KVO监听之前 - %p %p", [self.person1 methodForSelector:@selector(setName:)],
[self.person2 methodForSelector:@selector(setName:)]);
NSLog(@"person1添加KVO监听之后 - %p %p", [self.person1 methodForSelector:@selector(setName:)],
[self.person2 methodForSelector:@selector(setName:)]);
输出
person1添加KVO监听之前 - 0x10b804510 0x10b804510
person1添加KVO监听之后 - 0x7fff258e454b 0x10b804510
可见 person1的方法实现地址变了
- KVO生成的class对象的isa 指向KVO 生成的class对象的元类,不会指向被观察的对象的元类
KVO 对象里面的方法
- class方法
NSLog(@"person1添加KVO监听之后 - %s %s", object_getClassName(self.person1),
object_getClassName(self.person2));
NSLog(@"person1添加KVO监听之后 - %@ %@", [self.person1 class],
[self.person2 class]);
输出结果
NSKVONotifying_Person Person
Person Person
是不一样的
KVO的对象的class方法重写了 是为了隐蔽 NSKVONotifying_Person
屏蔽内部实现
探究2
- (NSString*)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);
return methodNames;
}
NSString * names = [self printMethodNamesOfClass:[self.person1 class]];
NSLog(@"printMethodNamesOfClass = %@",names);
Class realclass = NSClassFromString([NSString stringWithFormat:@"%s",object_getClassName(self.person1)] );
NSString * names2 = [self printMethodNamesOfClass:
realclass];
NSLog(@"printMethodNamesOfClass = %@",names2);
输出结果
printMethodNamesOfClass = .cxx_destruct, name, setName:,
printMethodNamesOfClass = setName:, class, dealloc, _isKVOA,
可见变成kvo对象了
总结
1.iOS用什么方法实现对一个对象的KVO?本质是什么?
- 利用RuntimeAPI动态生成的一个子类,并且让instance对象的isa指向这个全新的子类
- 当修改instance对象属性时,会调用Foundation的 _NSSetXXXValueAndNotify函数
willChangeValueForKey:
父类原来的setter
didChangeValueFoeKey:
内部会触发监听器(Oberser)的监听方法(-observeValueForKeyPath:ofObject:change:context:)
2.手动触发KVO?
[self.person1 willChangeValueForKey:@"name"];
[self.person1 didChangeValueForKey:@"name"];
3.直接修改成员变量会触发KVO么?
@interface Person:NSObject
{
@public
int _age ;
}
Person * p =[ [Person alloc]init];
p->_age = 2;
这种不会触发 因为没有走setter
网友评论