底层原理day04

2021-01-16  本文已影响0人  武当霍元甲

底层原理day04:

【KVO本质】
(key value observing)Rx响应式编程
背景:[self.person1 addObserver:self keyPath:@“age” options:xx context:nil】
【observeValueForKeyPath:ofObject:change:context:】
person1添加了KVO监听age属性
lldb:p self.person1.isa
打印出:NSKVONotifying_MJPerson

lldb:p self.person2.isa
打印出:MSPerson

instance的isa是指向的class对象。
NSKVONotifying_MJPerson类是runtime动态创建的一个类,是MJPerson的子类(这个类的superclass指针指向了MJPerson)
MJPerson中有isa、superclass、setAge:方法、age:方法、等等
NSKVONotifying_MJPerson类中有isa、superclass、setAge、class、dealloc、_isKVOA等等

所以self.person1.age = 10的原理是:
instance对象的isa先找到class对象NSKVONotifying_MJPerson,然后查找里面的setAge方法,
然后里面做了一下事情,做完之后再调用super去调用MJPerson里面的setAge方法

NSKVONotifying_MJPerson里面的setAge做了什么?:
里面调用了NSFoundation框架里面的_NSSetIntValueAndNotify()方法

伪代码:
void _NSSetIntValueAndNotify() {
[self willChangeValueForKey:@“age”];
[super setAge:age];
[self didChangeValueForKey:@“age”];
}

那么didChangeValueForKey方法里面又做了什么呢?
-(void) didChangeValueForKey:(NSString *)key {
// 通知监听器,某某属性值发生了改变
[observer obserValueForKeyPath:key ofObject:xx change:xxx context:xxx];
}

验证前面的说法:
在添加KVO方法(self.person1 addObserver:self forKeyPath:@“age” xxx)前后增加打印
NSLog(@“%p”, object_getClass(self.person1)) // 打印出MJPerson
self.person1 addObserver:xxxx
NSLog(@“%p”, object_getClass(self.person1)) // 打印出NSKVONotifying_MJPerson

现在我想看看setAge方法在添加KVO前后的变化:
NSLog(@“%p”, [self.person1 methodForSelector:@selector(setAge:));
self.person1 addObserver:xxxx
NSLog(@“%p”, [self.person1 methodForSelector:@selector(setAge:));
methodForSelector返回IMP地址。
打印出来,发现IMP地址不一样了

p (IMP)0x106683838把上面添加完observer之后的NSLog打印出来的内容调试打印一下,发现
(Foundation’ _NSSetIntValueAndNotify)

那么,这个新生成的NSKVONotifying_MJPerson的isa又指向什么呢?class对象的isa是指向meta-class的。

NSKVONotifying_MJPerson类里面还重写了class、dealloc方法,并实现了_isKVOA方法

-(Class)class {
// 伪了隐藏NSKVONotifying_MJPerson这个类。当别人调用instance的class方法时候,返回父类
}

-(void)dealloc{
// 做一些收尾工作
}

-(BOOL)_isKVOA {
return YES;
}

面试题:
1、用什么方式实现KVO的(KVO的本质是什么?)
a、使用Runtime API动态生成一个子类,(具体哪个runtime方法?)
b、这时候,instance的isa指向这个新生类,新生类的superclass指针指向原来的类
C、重写了setter方法,setter方法里面调用了Foundation框架的_NSSetxxValueAndNotify方法,xxx就是Int、Double、String等
d、_NSSetxxValueAndNotify方法具体会调用willChageValueForKey:、父类的setter方法、didChangeValueForKey:方法
f、didChangeValueForKey:方法里面会调用observer监听器的observerValueForKeyPath:ofObject:change:context方法。

如何手动触发KVO:下面两个方法同时调用,蒙骗系统。这样其实会调用派生类的willchang和didchang方法。但是其实value并没有发生改变
self.person1 willchangeValueForKey:
self.person1 didChangeValueForKey:

直接修改成员变量会触发KVO吗?:
比如成员变量暴露出来(不用@property)直接访问:self.person1->age = 2
那么不会触发KVO,因为从KVO本质,就是为了重写了setter方法,但是直接访问没有走setter方法。
可以手动触发:
self.person1 willchangeValueForKey:
self.person1->age = 2
self.person1 didChangeValueForKey:

【KVC】
Key-value coding:键值编码
主要API:key只能访问当前对象的属性。keypath则可以层层访问(访问当前对象属性的属性)
setValue:forKey:
setValue:forKeyPath
valueForKey:
valueForKeyPath:

面试题1:通过KVC修改属性,会触发KVO吗?([self.person setValue:@10 forKey:@“age”]会触发KVO吗)
答案:KVC修改,必定会触发KVO方法调用(即使是直接访问成员变量,即使没有重写setKey方法),因为KVC的setValue forKey和forKeyPath的内部,会帮忙调用KVO相关操作(内部帮忙调用willChangeForKey和didChangeForKey方法,重写这两个方法,就可以看到会帮忙调用)

面试题2:KVO的赋值和取值过程是怎样的。原理是什么?

KVC赋值的原理:
setValue:forKey:方法做了什么?(什么过程?)
首先会找instance的setKey:方法的实现,接着找_setKey:方法的实现。如果找到了,就传值并调用
如果没有找到,则查看+accessInstanceValiablesDirectly的返回值(是否允许直接访问成员变量)

KVC取值的原理:
valueForKey:方法做了什么?(什么过程?)
首先按照getKey、key、isKey、_key的顺序查找是否有方法。如果有,则直接调用,取值成功(获得返回值)
如果四个方法都没找到。则查看+accessInstanceValiablesDirectly的返回值(是否允许直接访问成员变量)

上一篇下一篇

猜你喜欢

热点阅读