美文网首页
底层原理day04

底层原理day04

作者: 武当霍元甲 | 来源:发表于2021-01-16 16:04 被阅读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的返回值(是否允许直接访问成员变量)

    • 如果方法+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)

    相关文章

      网友评论

          本文标题:底层原理day04

          本文链接:https://www.haomeiwen.com/subject/fcbnaktx.html