学习笔记:iOS底层原理day04

139 阅读4分钟

【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的返回值(是否允许直接访问成员变量)

  • 如果方法+accessInstanceValiablesDirectly返回值为NO,则会调用setValue:forUndefinedKey:方法。如果方法还是没有实现,则抛出异常(NSUnknownKeyException)
  • 如果方法+accessInstanceValiablesDirectly返回值为YES,则会按照_key、_isKey、key、isKey的顺序查找成员变量。如果找到了,直接给成员变量赋值,如果没找到任意一个成员变量。还是会调用setValue:forUndefinedKey:方法,如果方法没实现,则抛出异常(NSUnknownKeyException)

KVC的疑问:1、accessInstanceValiablesDirectly方法是系统的方法吗?有默认值吗?2、setValue:forUndefinedKey:如果实现了,是否就不会抛出异常了。

accessInstanceValiablesDirectly默认的返回值就是YES

KVC取值的原理:

valueForKey:方法做了什么?(什么过程?)

首先按照getKey、key、isKey、_key的顺序查找是否有方法。如果有,则直接调用,取值成功(获得返回值)

如果四个方法都没找到。则查看+accessInstanceValiablesDirectly的返回值(是否允许直接访问成员变量)

  • 如果方法+accessInstanceValiablesDirectly返回值为NO,则会调用valueForUndefinedKey方法,如果方法还是没有实现,则抛出异常(NSUnkownKeyException)
  • 如果方法+accessInstanceValiablesDirectly返回值为YES,则会按照_key、_isKey、key、isKey的顺序查找成员变量。如果找到,里面的值直接取出来。如果四个都没找到,则会调用valueForUndefinedKey方法,如果方法还是没有实现,则抛出异常(NSUnkownKeyException)