• KVC与KVO的实现原理


    |KVC的用法

    1、KVC既键值编码(Key Value Coding),基于NSKeyValueCoding协议,它是以字符串的形式来操作对象的成员变量,也就是通过字符串key来指定要操作的成员变量。基本操作如:

    • setValue:forKey:为成员变量赋值。如:[student setValue:@"大明" forKey:@"name"];
    • valueForKey:获取指定的成员变量。如:NSString *name = [student valueForKey:@"name"];

    2、用KVC操作成员变量的底层实现,就拿成员变量name来说吧,一般按如下顺序执行:

    • 程序先尝试调用name的setter或getter方法,就是先调用setName或name方法来设置或获取成员变量。
    • 如果该类没有提供setter或者getter方法,则KVC机制将会搜索该类中名为_name的成员变量。这时无论该成员变量定义在接口部分还是实现部分,KVC的代码底层都会对_name进行赋值或者获取操作。
    • 如果该类既没有setter或者getter方法,也没有_name成员变量,则KVC机制将会搜索该类中名为name的成员变量。这时无论该成员变量定义在接口部分还是实现部分,KVC的代码底层都会对name进行赋值或者获取操作。
    • 如果以上3步都没能找到指定的成员变量,则程序将会执行setValue:forUndefinedKey:或者valueForUndefinedKey:方法抛出异常,默认是终止程序。

    3、对于不存在的key怎么处理呢?默认情况下会抛出NSUnknownKeyException异常,并终止程序。

    此时可以直接在类的实现部分重写setValue:forUndefinedKey:和valueForUndefinedKey:方法,当KVC找不到指定的成员变量时,会调用这两个方法,通过重写这两个方法可以方便的定制自己的处理方案。

    4、setNilValueForKey:方法的调用。

    当用KVC的方式把基本数据类型(如int、float等)设置为nil时,会调用成员变量所属类的setNilValueForKey:方法,抛出NSInvalidArgumentException异常,并终止程序。

    此时在该类的实现中重写setNilValueForKey:做相应的处理即可。

    5、KVC操作对象的复合属性。

    复合属性:一个类的属性是另一个类的对象,这个对象的属性就是就是复合属性,比如Person类有个Student类型的属性student,Student类有个name属性,name相对于Person类就是个复合属性。这个复合属性在KVC机制中被称为key路径,即student.name。

    在KVC机制中操作key路径的方法如下:

    • setValue:forKeyPath:根据key路径设置属性值。
    • valueForKeyPath:根据key路径获取属性值。

    通过KVC操作对象的性能比通过setter和getter方法要差,但是通过KVC比较灵活。

    |KVO的用法

    KVO即键值监听(Key Value Observe),基于NSKeyValueObserving协议,用于监听属性值的改变(通常是数据模型)。

    KVO的用法分三步:

    1. 为对象指定的key路径注册监听器 -> addObserver:forKeyPath:options:context:。
    2. 重写监听方法以得到最新修改的数据 -> observeValueForKeyPath:ofObject:change:context:。
    3. 删除指定key路径的监听器 -> removeObserver:forKeyPah:或者removeObserver:forKeyPath:context。

    参数说明:

    observer: 观察对象。

    forKeyPath:对象的复合属性。

    options分别是:

    • NSKeyValueObservingOptionNew:把更改之前的值提供给处理方法
    • NSKeyValueObservingOptionOld:把更改之后的值提供给处理方法
    • NSKeyValueObservingOptionInitial:把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
    • NSKeyValueObservingOptionPrior:分2次调用,在值改变之前和值改变之后。
    • 0:就代表不带任何参数进去。

    context: 可以带入一些参数,其实这个挺好用的,任何类型都可以,自己强转就好了。

    change:  字典里的key对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等。

    •  1 #import "ViewController.h"
       2 #import "Person.h"
       3 #import "Student.h"
       4 
       5 
       6 @interface ViewController ()
       7 @property (strong, nonatomic) Person *person;
       8 @property (strong, nonatomic) Student *stu;
       9 @end
      10 
      11 @implementation ViewController
      12 - (void)viewDidLoad {
      13     [super viewDidLoad];
      14     Person *person = [[Person alloc] init];
      15     self.person = person;
      16     Student *stu = [[Student alloc] init];
      17     self.stu = stu;
      18     person.height = 1.5;
      19     person.student = stu;
      20     stu.height = 1.0;
      21     stu.age = 10;
      22     [person addObserver:self forKeyPath:@"height" options:NSKeyValueObservingOptionNew context:nil];
      23     [person addObserver:self forKeyPath:@"student.height" options:NSKeyValueObservingOptionNew context:nil];
      24     [stu addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
      25     for (int i = 1; i < 4; i++) {
      26         [person setValue:@(person.height + i) forKey:@"height"];
      27         [person setValue:@(person.student.height + i) forKeyPath:@"student.height"];
      28         [stu setValue:@(stu.age + i) forKey:@"age"];
      29     }
      30     
      31     NSLog(@":person.height = %@,    stu.height = %@,    stu.age = %@", [person valueForKey:@"height"], [person valueForKeyPath:@"student.height"], [stu valueForKey:@"age"]);
      32 }
      33 
      34 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
      35     NSLog(@"
      --------------------
      被修改的KeyPath:%@
      被修改的对象:%@
      被修改后的值:%@
      被修改的上下文:%@
      
      
      ", keyPath, object, change[NSKeyValueChangeNewKey], context);
      36 }
      37 
      38 - (void)dealloc {
      39     [self.person removeObserver:self forKeyPath:@"height"];
      40     [self.person removeObserver:self forKeyPath:@"student.height"];
      41     [self.stu removeObserver:self forKeyPath:@"age"];
      42 }
      43 
      44 @end
       
  • 相关阅读:
    Koa初探
    npm 安装electron 失败的问题和解决办法
    vscode常用快捷键
    docker 执行TP 的 migrate数据库迁移报错2002
    git删除历史记录中的大文件详解
    js replace 替换路径无效的问题
    window.prompt()方法数据类型问题
    scorecardpy 介绍
    CatBoost 使用
    webgl
  • 原文地址:https://www.cnblogs.com/hankkk/p/5747333.html
Copyright © 2020-2023  润新知