• KVC讲解


    今天趁着项目bug修复完了,来讲解一下OC知识的另一个技术点-KVC!针对KVC,讲解两个知识点

    • 通过KVC修改属性会触发KVO么?
    • KVC的赋值过程是怎样的?原理是什么?
    • KVC的取值过程是怎样的?原理是什么?

    一、问:通过KVC修改属性会触发KVO么?

    答:会触发KVO

    创建工程项目TestKVO,ZXYPerson类有一个属性age,在控制器ViewController中添加属性观察者KVO,项目代码如下

    @interface ViewController ()
    @property(nonatomic,strong) ZXYPerson
    *p; @end

    @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _p = [[ZXYPerson alloc]init]; _p.age = 10; [_p addObserver:self forKeyPath:@"age" options: NSKeyValueObservingOptionNew context:nil]; [_p setValue:@12 forKeyPath: @"age"]; } -(void)dealloc { [_p removeObserver:self forKeyPath:@"age"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ NSLog(@"*********%@", change); }

    上面橙色文字通过KVC方式更改属性的值,将上面代码运行结果如下:

    通过上面发现setValue:forKeyPath触发了KVO,同理发现setValue:forKey也会触发KVO,但是这两个方法有什么区别呢?

    setValue:forKeyPath会一层一层的(沿着路径)向下找,然而setValue:forKey并不会这样!(假如ZXYPerson养了一只猫,猫有age属性 ,通过"_p.cat.age"设置应该用setValue:forKeyPath,不能用setValue:forKey

    思考: 为什么KVC更改属性值会触发KVO?那就需要讲解下面知识。

    二、问:KVC的赋值过程是怎样的?原理是什么?

     setValue:forKey:的原理

    accessInstanceVariablesDirectly方法的默认返回值是YES

    下面一一验证上面的顺序:

     验证setValue:forkey调用过程不需要用到KVO,去除多余的代码之后,简化成如下:

    #import "ViewController.h"
    #import "ZXYPerson.h"
    
    @interface ViewController ()
    
    @property(nonatomic,strong) ZXYPerson *p;
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        _p = [[ZXYPerson alloc]init];
        [_p setValue:@12 forKeyPath: @"age"];
        
    }
    
    @end
    
    
    #import "ZXYPerson.h"
    
    @implementation ZXYPerson
    
    - (void) setAge:(int)age {
        NSLog(@"调用了setAge方法");
    }
    
    - (void) _setAge: (int)age {
        NSLog(@"调用了_setAge方法");
    }
    
    @end

    去除了age属性的声明,看看KVC赋值的前期过程(按照setKey, _setKey方法走)

     同时写了两个方法,优先调用setAge方法,假如将setAge方法注释掉

     注释掉setAge方法后,久调用了_setAge方法,证实了KVC的前期赋值情况!

    如果两个方法都没有实现,此时KVC会accessInstanceVariablesDirectly方法,返回Yes代表可以直接访问成员变量,反之不能访问成员变量!

    如果返回为Yes,会按照_key、_isKey、key、isKey成员属性进行赋值

    此时像上面的代码加入这四个成员变量,如下(前提accessInstanceVariablesDirectly方法返回Yes)

    @interface ZXYPerson : NSObject
    {
        @public
        int _age;
        int _isAge;
        int age;
        int isAge;
    }
    
    @end

    加入上述代码,运行

     首先给_age赋值,当四个成员变量同时出现,假如将int _age成员变量注释掉,如下:

    发现当_age注释掉之后,优先给_isAge赋值,优先级仅次于_age,假如将_isAge注释掉之后

     发现给age赋值,同理将age成员变量注释掉之后

     最后给isAge赋值,符合了上述setValue:forkey的访问属性的优先级 _key > _isKey > key > isKey的顺序

    如果这四个成员变量都没有了,就会报异常

     通过上面讲述知道setValue:forKey会触发KVO

    [_p setValue:@12 forKeyPath: @"age"]内部调用相当于

    [p willChangeValueForKey @"age"]

    p->_age = 12;

    [p didChangeValueForKey @"age"]

    所以会触发KVO

    以上就是setValue:forKey的赋值所有过程,希望大家再看看上述图,下面讲述KVC如何取值?

    三、问: KVC的取值过程是怎样的?原理是什么?

    valueForKey:的原理

     下面一一验证上面的顺序:

    @interface ZXYPerson : NSObject{
        @public
        int _age;
    }
    
    @implementation ZXYPerson
    
    - (int)getAge {
        return 11;
    }
    
    - (int)age {
        return 12;
    }
    
    - (int)isAge {
        return 13;
    }
    
    - (int)_age {
        return 14;
    }
    
    @end
    
    
    @interface ViewController ()
    @property(nonatomic,strong) ZXYPerson *p;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        _p = [[ZXYPerson alloc]init];
        _p->_age = 10;
        
        NSLog(@"******%@",[_p valueForKey:@"age"]);
        
    }
    
    @end

    看看KVC取值的前期过程(按照getAge > age > isAge > _age 方法走)

    当有四个方法时,会优先调用getAge方法,如上面一样打印出11,调用了getAge方法!假如把getAge()方法注释掉,运行代码:

     将getAge()方法注释掉后,调用了age方法,验证了getAge > age !假如把age方法注释掉

    将getAge()和age()方法注释掉后,调用了isAge()方法,验证了getAge > age > isAge !假如把isAge()方法注释掉 

    将getAge()和age()方法以及isAge()注释掉后,调用了_age()方法,验证了getAge > age > isAge > _age! 

    如果四个方法都没有实现,此时KVC会看accessInstanceVariablesDirectly方法,返回Yes代表可以直接查找成员变量,反之不能查找成员变量!

    如果返回为Yes,会按照_key、_isKey、key、isKey成员属性顺序查找成员变量

    此时像上面的代码加入这四个成员变量,如下(前提accessInstanceVariablesDirectly方法返回Yes,去除四个方法)

        @public
        int _age;
        int _isAge;
        int age;
        int isAge;

    加入了四个成员变量,控制器ViewController加入设置属性的四个值的

        _p->_age = 11;
        _p->_isAge = 12;
        _p->age = 13;
        _p->isAge = 14;

    观察成员变量的查找顺序!验证_key、_isKey、key、isKey

     ZXYPerson有四个成员变量,当向着上面代码书写,运行代码结论是11,对应着_age这个成员变量,所以优先取值_age!当将 _age成员变量注释掉以及赋值注释掉后

    发现运行结果为12,对应的结果时_isAge, 得出结论 _age > _isAge! 继续将_isAge成员变量注释掉以及赋值_isAge如下:

     发现运行结果为13,对应的结果时age, 得出结论 _age > _isAge > age! 继续将age成员变量注释掉以及赋值age如下:

     发现运行结果为14,对应的结果时isAge, 得出结论 _age > _isAge > age > isAge! 继续将isAge成员变量注释掉以及赋值isAge如下:

     如果都注释掉,会报异常valueForUndefinedKey错误!

    以上就是valueForKey的取值所有过程,希望大家再看看上述图回顾KVC如何取值!

    上述就是KVC的基本内容,希望对大家有所帮助,可以关注博客会实时更新,谢谢!!!

  • 相关阅读:
    Pytorch环境搭建(Anaconda+Pycharm,清华镜像源)
    Leetcode 220. 存在重复元素 III (Contains Duplicate III)
    Leetcode 217. 存在重复元素 (Contains Duplicate)
    Leetcode 219. 存在重复元素 II (Contains Duplicate II)
    Leetcode 1073. 负二进制数相加(Adding Two Negabinary Numbers)
    极客战记(codecombat)攻略-地牢-Kithgard 地牢
    python elasticsearch 深度分页——scroll的使用与清除(clear_scroll)
    sqlmap参数
    文件包含
    web安全
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/12663910.html
Copyright © 2020-2023  润新知