美文网首页
KVO 的相关问题

KVO 的相关问题

作者: 笔头还没烂 | 来源:发表于2023-04-12 16:05 被阅读0次
  1. iOS 中,实例对象添加了 KVO 监听之后,该实例对象调用 class 方法和执行 object_getClass 并传入该实例对象有什么区别?

    结论:调用实例对象的 class 方法会返回该对象所属的类,而执行 object_getClass 并传入该实例对象会返回该对象的 isa 指针所指向的类。在普通的情况下,这两者是相同的。但是如果该实例对象所属的类是一个KVO子类,那么调用 class 方法会返回该实例对象原始的类,而执行 object_getClass 并传入该实例对象会返回 KVO 子类。这是因为 KVO 会通过运行时机制动态生成一个子类来代理原始类,从而实现 KVO 监听。
    查看了苹果 runtime 的 NSObject 的 - (Class)class 官方源码,源码如下:

    - (Class)class {
        return object_getClass(self);
    }
    
    • 可见,NSObject 的 class 实例方法的底层实现就是走了 object_getClass 方法。
    • 通过代码测试,当实例对象添加了 KVO 监听之后,调用 class 会返回该实例对象原始的类。由此可见,添加了 KVO 监听的实例对象的 isa 指向一个通过 runtime 机制动态生成的子类类对象,该类对象的方法列表中重写了 class 方法,返回了原始类,从而屏蔽了内部实现,隐藏了 NSKVONotifying_XXX 类的存在。
  2. 怎么证明 Person 实例对象 p1 添加了 KVO 监听之后,程序动态创建一个 NSKVONotifying_Person 类是 Person 类的子类?

    • 我们知道类对象的 superclass 指针指向它的父类的类对象,因此可以通过 superclass 指针来获取。示例代码如下:
    #import "ViewController.h"
    #import <objc/runtime.h>
    
    struct gq_object_class {
        Class isa;
        Class superclass;
    };
    
    @interface Person : NSObject
    @property (nonatomic,assign) int age;
    @end
    
    @implementation Person
    
    @end
    
    @interface ViewController ()
    @property (nonatomic,strong) Person *person1;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.person1 = [[Person alloc] init];
        self.person1.age = 10;
        
        //给 person 对象添加 KVO监听
        [self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.person1.age = 20;
        struct gq_object_class *p1Cls = (__bridge struct gq_object_class *)object_getClass(self.person1);
        NSLog(@"%p",p1Cls->superclass);
    }
    
    //当监听对象对象的属性值发生改变时,就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"监听到 %@ 的 %@ 属性值改变了 -- %@",object,keyPath,change);
    }
    
    - (void)dealloc {
        [self.person1 removeObserver:self forKeyPath:@"age"];
    }
    @end
    

    程序运行,当点击手机屏幕时,控制台输出地址值,通过 po 打印该地址值所指向的对象,如下所示:

    0x100a61168

    (lldb) po 0x100a61168

    Person

  1. iOS 用什么方式实现对一个对象的 KVO?(KVO 的本质是什么)

    利用 RuntimeAPI 动态生成一个子类,并且让 instance 对象的 isa 指向这个全新的子类;

    当修改 instance 对象的属性时,会调用属性的 setter 方法,并在该 setter 方法中调用 Foundation 的 _NSSetXXXValueAndNotify 函数。_NSSetXXXValueAndNotify函数内部主要做下面三件事情:

    • 调用实例对象的 willChangeValueForKey: 方法;
    • 调用父类原来的 setter 方法;
    • 调用实例对象的 didChangeValueForKey: 方法
      • didChangeValueForKey: 方法内部又会触发监听器(Observer)的监听方法(ObserveValueForKeyPath:ofObject:change:context:)

    伪代码如下所示:
    (1)NSKVONotifying_Person.h

    #import "MJPerson.h"
    @interface NSKVONotifying_Person : MJPerson
    @end
    

    (2)NSKVONotifying_Person.m

    #import "NSKVONotifying_Person.h"
    @implementation NSKVONotifying_Person
    - (void)setAge:(int)age
    {
        _NSSetIntValueAndNotify();
    }
    // 伪代码
    void _NSSetIntValueAndNotify()
    {
        [self willChangeValueForKey:@"age"];
        [super setAge:age];
        [self didChangeValueForKey:@"age"];
    }
    - (void)didChangeValueForKey:(NSString *)key
    {
        // 通知监听器,某某属性值发生了改变
        [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
    }
    @end
    
  2. 如何手动触发 KVO ?

    问题描述:KVO 的使用场景一般是当值被修改了,系统会自动帮我们调用 KVO 的监听方法(自动触发);现在想实现的是,在值没有被修改的情况下,KVO 的监听方法也能被我们调用(手动触发)。

    示例代码如下:

    #import "ViewController.h"
    #import <objc/runtime.h>
    #import "Person.h"
    
    @interface ViewController ()
    @property (nonatomic,strong) Person *person1;
    @property (nonatomic,strong) Person *person2;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.person1 = [[Person alloc] init];
        self.person1.age = 10;
        
        self.person2 = [[Person alloc] init];
        self.person2.age = 5;
        
        //给 person 对象添加 KVO监听
        [self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [self.person1 willChangeValueForKey:@"age"];
        [self.person1 didChangeValueForKey:@"age"];
    }
    
    //当监听对象对象的属性值发生改变时,就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"监听到 %@ 的 %@ 属性值改变了 -- %@",object,keyPath,change);
    }
    
    - (void)dealloc {
        [self.person1 removeObserver:self forKeyPath:@"age"];
    }
    @end
    

    当程序运行后点击手机屏幕时,输出结果如下:

    监听到 <Person: 0x600002284170> 的 age 属性值改变了 -- {

    kind = 1;

    new = 10;

    old = 10;

    }

相关文章

  • iOS崩溃收集相关 SIGSEGV Background Tas

    iOS崩溃收集相关 SIGSEGV Background Task 崩溃的主要分类 我们可以看到, KVO 问题、...

  • KVO相关

    KVO的实现原理: 核心是重写子类的set方法进行监听 具体是:当某一个类在注册监听的时候,系统会动态(1)创建一...

  • KVO 相关

    1 . KVO 的基本使用kvo "键值监听", 全称"Key-Value Observing" ,监听对象属性...

  • 动手实现 KVO

    动手实现 KVO 相关链接:http://tech.glowing.com/cn/implement-kvo/ 概...

  • iOS-KVO相关

    KVO相关 一、KVO初探 — 响应观察 (一)KVO 使用 的 三部曲 1、添加观察 2、响应 3、析构 (...

  • 04. KVO使用,原理,本质

    问题 KVO日常使用 KVO原理(KVO本质是什么) 如何手动触发KVO 直接修改成员变量会触发KVO吗 KVO图...

  • [iOS面试]第2章 Objective-C语言特性相关面试问题

    本文主讲Objective-C语言特性相关面试问题,包括分类、关联对象、扩展、代理、通知、KVO、 KVC、 属性...

  • Swift KVO相关

    前言 在写swift版的上下拉刷新,语法看了3个小时就开始动手了,结果一路坑,先写几篇铺垫的文章,后续文章会附上上...

  • KVO 相关总结

    1.Objective-C 中的键(key)-值(value)观察(KVO)并不是什么新鲜事物,它来源于设计模式中...

  • KVO相关整理

    下边三种方式都可以: 触发监听 重写set方法事,不需要调用willChangeValueForKey和didCh...

网友评论

      本文标题:KVO 的相关问题

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