美文网首页
iOS KVO 复习

iOS KVO 复习

作者: 笔头还没烂 | 来源:发表于2023-04-11 23:50 被阅读0次
  1. KVO 的全称是 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变

  2. 简单应用的示例代码:

    • Person 类:

      @interface Person : NSObject
      @property (nonatomic,assign) int age;
      @end
      @implementation Person
      @end
      
    • ViewController 类:

      #import "ViewController.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 = 20;
          
          //给 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;
          self.person2.age = 30;
      }
      
      //当监听对象对象的属性值发生改变时,就会调用
      - (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: 0x6000002f45c0> 的 age 属性值改变了 -- {

      kind = 1;

      new = 20;

      old = 10;

      }

  3. KVO 本质探究

    • 为了看出问题所在,我们知道 runtime 中的 object_getClass 返回的是一个对象的 isa 指针,我们将 touchesBegan 方法修改一下,如下所示:

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
          self.person1.age = 20;
          self.person2.age = 30;
          
          NSLog(@"%p",object_getClass(self.person1));
          NSLog(@"%p",object_getClass(self.person2));
      }
      

      我们将断点打在最后一行的下面,当程序运行时,点击手机屏幕,程序运行停在断点处,输出结果如下:

      0x60000358c060

      0x1044fd248

      object_getClass( )方法如果传入的是实例对象,则返回的一个类的类对象,并且是惟一的。但是我们会惊讶地发现,上面拿到的却是两个地址值,说明是两个不同的类对象。为了看清楚这两个类对象分别对应什么类型,我们继续通过 lldb 动态调试器分别打印这两个地址值对应的类型,调试过程如下:

      (lldb) po 0x60000358c060

      NSKVONotifying_Person

      (lldb) po 0x1044fd248

      Person

      此时可以看到,没有添加 KVO 监听的 person2 类型还是 Person,而添加了 KVO 监听的 person1 类型则变成了 NSKVONotifying_Person。

      结论:由此可以看出,OC 在程序运行的时候,给添加了KVO 监听的实例对象动态创建了一个类,该类的类名为:NSKVONotifying_xxx。

    • 为了进一步验证我们的想法,我们手动在工程中新建一个类,类名为:NSKVONotifying_Person,重新运行此程序,Xcode 会提示:

      KVO failed to allocate class pair for name NSKVONotifying_Person, automatic key-value observing will not work for this class
      

      从另一个角度证明,当我们手动创建的类与程序在运行时要动态生成的类的类名冲突时,则系统无法完成KVO动态生成类的工作,此时我们的 KVO 的相关代码也不起作用。

相关文章

网友评论

      本文标题:iOS KVO 复习

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