记 上一篇文章中说到,KVO监听成员变量没法收到回调。先验证一下是否是对的。数组
1.建立一个Person
类,包含一个公有成员变量age
,一个属性变量name
。bash
@interface Person : NSObject{
@public
int age;
}
@property(strong,nonatomic)NSString * name;
复制代码
成员变量与属性的区别: 属性会自动生成setter
与getter
方法,成员变量不会。ui
2.在ViewController
中分别监听两个变量。atom
self.p = [[Person alloc]init];
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self.p addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionNew) context:NULL];
复制代码
3.实现监听回调,log
出改变内容以及被观察者。spa
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@---%@",change,object);
}
复制代码
4.分别给两个变量赋值,用于触发KVO
。code
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p.name = @"name";
self.p->age = 10;
}
复制代码
.
点语法与->
的区别: .
点语法调用了setter
方法,而->
是直接访问成员变量server
5.养成习惯,移除观察者对象
-(void)dealloc{
[self.p removeObserver:self forKeyPath:@"name"];
[self.p removeObserver:self forKeyPath:@"age"];
}
复制代码
6.触发KVO
后的发现log
内容只有一个继承
2019-03-07 21:53:13.105997+0800 KVO[84542:11106254] {
kind = 1;
new = name;
}---<Person: 0x6000007a6600>
复制代码
7.在@implementation
中手动为age
写setter
方法rem
-(void)setAge:(int)newAge{
age=newAge;
}
复制代码
8.修改touchesBegan
方法中的age
赋值方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p.name = @"name";
[self.p setAge:10];
}
复制代码
9.再次运行程序,触发KVO
,观察log
信息,会发现打印了两次。
2019-03-07 22:24:24.903100+0800 KVO[85477:11167107] {
kind = 1;
new = name;
}---<Person: 0x600002680a40>
2019-03-07 22:24:24.903477+0800 KVO[85477:11167107] {
kind = 1;
new = 10;
}---<Person: 0x600002680a40>
复制代码
因而可知,只有对象实现了setter
方法,KVO
对其进行的观察,才会发起回调。
分别在添加观察者先后打印一下对象名称
self.p = [[Person alloc]init];
NSLog(@"%s",object_getClassName(self.p));
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
NSLog(@"------分割线----------");
NSLog(@"%s",object_getClassName(self.p));
复制代码
打印结果为:
2019-03-08 10:13:13.099894+0800 KVO[88913:11408301] Person
2019-03-08 10:13:13.101816+0800 KVO[88913:11408301] ------分割线----------
2019-03-08 10:13:13.102021+0800 KVO[88913:11408301] NSKVONotifying_Person
复制代码
从打印结果咱们能够观察到,先前self.p
指向的是Person
,添加观察者后变成了NSKVONotifying_Person
。由此可知,在咱们为一个对象添加观察者以后,KVO
会自动建立一个NSKVONotifying_<ClassName>
。
这里有一个打印类跟子类的方法
-(void)printClasses:(Class)cls{
//注册类的总量
int count = objc_getClassList(NULL, 0);
//建立一个数组,其中包含给定对象
NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
//获取全部已经注册的类
Class *classes = (Class *)malloc(sizeof(Class)*count);
objc_getClassList(classes, count);
for (int i = 0; i<count; i++) {
//classes[i]的父类 等于 cls
if (cls == class_getSuperclass(classes[i])) {
[mArray addObject:classes[i]];
}
}
//释放classes
free(classes);
NSLog(@"%@",mArray);
}
复制代码
在添加观察者前,尝试打印一下[Person class]
[self printClasses:[Person class]];
复制代码
打印结果为:
2019-03-07 22:52:02.545406+0800 KVO[86213:11217129](
Person,
Student
)
复制代码
而后添加一个观察者,再从新打印一次
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self printClasses:[Person class]];
复制代码
打印结果为:
2019-03-07 22:52:02.555585+0800 KVO[86213:11217129] (
Person,
"NSKVONotifying_Person",
Student
)
复制代码
因此KVO
会自动建立的NSKVONotifying_<ClassName>
类继承自<ClassName>
。
这里有一个打印当前类执行的全部方法的方法
-(void)printClassAllMethod:(Class)cls{
unsigned int count = 0;
//获取当前类对应的方法列表
Method *methods = class_copyMethodList(cls, &count);
for (int i = 0 ; i<count; i++) {
Method method = methods[i];
//分别获取SEL 跟 IMP
SEL methodSEL = method_getName(method);
IMP methodIMP = class_getMethodImplementation(cls, methodSEL);
//打印SEL名称以及IMP地址
NSLog(@"%@---%p",NSStringFromSelector(methodSEL),methodIMP);
}
//释放methods
free(methods);
}
复制代码
观察先后若是SEL
数量发生改变,表明有对其进行了新添方法(为何没删除?子类没办法操做父类方法)。若是IMP地址发生了改变,表明对这个IMP
进行的重写。 验证: 新建一个Student
类继承Person
Person
中实现方法-(void)say;
Student
重写-(void)say;
而且实现方法-(void)study;
顺便重写一下原有方法 class
方法 分别打印-(void)printClassAllMethod:(Class)cls
[self printClassAllMethod:[Person class]];
NSLog(@"------分割线----------");
[self printClassAllMethod:[Student class]];
复制代码
打印结果:
2019-03-08 11:35:52.257273+0800 KVO[90824:11859981] say---0x109dfd260
2019-03-08 11:35:52.257500+0800 KVO[90824:11859981] ------分割线----------
2019-03-08 11:35:52.257659+0800 KVO[90824:11859981] say---0x109dfd1c0
2019-03-08 11:35:52.257774+0800 KVO[90824:11859981] study---0x109dfd1f0
2019-03-08 11:35:52.257976+0800 KVO[90824:11859981] class---0x109dfd220
复制代码
其中, say
方法重写,IMP
地址发生改变,而且打印出了新增的study
方法以及重写的class
方法。
分别打印[Person class]
与[NSKVONotifying_Person class]
,对比原类跟新类都执行了什么方法。
[self printClassAllMethod:[Person class]];
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
NSLog(@"------分割线----------");
[self printClassAllMethod:NSClassFromString(@"NSKVONotifying_Person")];
复制代码
打印结果:
2019-03-08 10:47:01.419689+0800 KVO[89658:11656677] .cxx_destruct---0x10b19c1b0
2019-03-08 10:47:01.419936+0800 KVO[89658:11656677] name---0x10b19c150
2019-03-08 10:47:01.420134+0800 KVO[89658:11656677] setName:---0x10b19c170
2019-03-08 10:47:01.421109+0800 KVO[89658:11656677] ------分割线----------
2019-03-08 10:47:01.421352+0800 KVO[89658:11656677] setName:---0x10b4f663a
2019-03-08 10:47:01.421516+0800 KVO[89658:11656677] class---0x10b4f506e
2019-03-08 10:47:01.421649+0800 KVO[89658:11656677] dealloc---0x10b4f4e12
2019-03-08 10:47:01.421782+0800 KVO[89658:11656677] _isKVOA---0x10b4f4e0a
复制代码
因此,NSKVONotifying_Person
重写了setter
、class
、dealloc
添加了_isKVOA
。
setter
方法,KVO
对其进行的观察,才会发起回调。KVO
会自动建立一个NSKVONotifying_<ClassName>
。NSKVONotifying_<ClassName>
类继承自<ClassName>
。NSKVONotifying_<ClassName>
重写了setter
、class
、dealloc
,添加了_isKVOA
。