美文网首页iOS
iOS-KVC 底层原理

iOS-KVC 底层原理

作者: Summit_yp | 来源:发表于2021-08-13 14:01 被阅读0次

    先上一波KVC的官方文档

    setValue:forKey:的搜索流程:

    setValue:forKey:搜索流程

    -【第一步】首先查找是否有这三种setter方法,按照查找顺序为set<Key>:-> _set<Key> -> setIs<Key>(文档中没有提到,后文会验证

    如果有其中任意一个setter方法,则直接设置属性的value(主注意:key是指成员变量名,首字符大小写需要符合KVC的命名规范)

    如果都没有,则进入【第二步】

    -【第二步】:如果没有第一步中的三个简单的setter方法,则查找accessInstanceVariablesDirectly是否返回YES,

    如果返回YES,则查找间接访问的实例变量进行赋值,查找顺序为:_<key> -> _is<Key> -> <key> -> is<Key>
    如果找到其中任意一个实例变量,则赋值

    如果都没有,则进入【第三步】

    如果返回NO,则进入【第三步】
    -【第三步】如果setter方法 或者 实例变量都没有找到,系统会执行该对象的setValue:forUndefinedKey:方法,默认抛出NSUndefinedKeyException类型的异常

    验证一下第一步的问题:
    定义如下类

    @interface YPPerson ()
    {
      @public
      NSString *name;
      NSString *_name;
      NSString *isName;
      NSString *_isName;
    }
    
    @end
    
    @implementation YPPerson
    //挨个注释以下set方法,确认setValue:forKey:是否会调用
    - (void)setName:(NSString *)name
    {
      NSLog(@"%s",__func__);
    }
    
    - (void)_setName:(NSString *)name
    {
      NSLog(@"%s",__func__);
    }
    
    - (void)setIsName:(NSString *)name
    {
      NSLog(@"%s",__func__);
    }
    //不会调用这个
    - (void)_setIsName:(NSString *)name
    {
      NSLog(@"%s",__func__);
    }
    
    

    通过以下代码调用setValue:forKey:

      YPPerson *person = [[YPPerson alloc]init];
      [person setValue:@"YP" forKey:@"name"];
    

    文档中提到的setName,_setName就不验证了,我们直接注释掉,运行代码,结果如下

    image.png
    证明官方文档中确实少写了这个方法。嘿嘿,被逮住了吧。
    setValue流程图

    接下来看看valueForKey:

    valueForKey:流程

    翻译一下
    -【第一步】首先查找getter方法,按照get<Key> -> <key> -> is<Key> -> _<key>的方法顺序查找,
    如果找到,则进入【第五步】

    如果没有找到,则进入【第二步】

    -【第二步】如果第一步中的getter方法没有找到,KVC会查找countOf <Key>和objectIn <Key> AtIndex :和<key> AtIndexes :
    如果找到countOf <Key>和其他两个中的一个,则会创建一个响应所有NSArray方法的集合代理对象,并返回该对象,即NSKeyValueArray,是NSArray的子类。代理对象随后将接收到的所有NSArray消息转换为countOf<Key>,objectIn<Key> AtIndex:和<key>AtIndexes:消息的某种组合,用来创建键值编码对象。如果原始对象还实现了一个名为get<Key>:range:之类的可选方法,则代理对象也将在适当时使用该方法(注意:方法名的命名规则要符合KVC的标准命名方法,包括方法签名。)

    如果没有找到这三个访问数组的,请继续进入【第三步】

    -【第三步】如果没有找到上面的几种方法,则会同时查找countOf <Key>,enumeratorOf<Key>和memberOf<Key>这三个方法
    如果这三个方法都找到,则会创建一个响应所有NSSet方法的集合代理对象,并返回该对象,此代理对象随后将其收到的所有NSSet消息转换为countOf<Key>,enumeratorOf<Key>和memberOf<Key>:消息的某种组合,用于创建它的对象

    如果还是没有找到,则进入【第四步】

    -【第四步】如果还没有找到,检查类方法InstanceVariablesDirectly是否YES,依次搜索_<key>,_is<Key>,<key>或is<Key>的实例变量
    如果搜到,直接获取实例变量的值,进入【第五步】
    -【第五步】根据搜索到的属性值的类型,返回不同的结果
    如果是对象指针,则直接返回结果

    如果是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回它

    如果是是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象

    -【第六步】如果上面5步的方法均失败,系统会执行该对象的valueForUndefinedKey:方法,默认抛出NSUndefinedKeyException类型的异常


    valueForKey流程图

    KVC 使用场景
    1、动态设值和取值

    常用的可以通过setValue:forKey: 和 valueForKey:

    也可以通过路由的方式setValue:forKeyPath: 和 valueForKeyPath:

    2、通过KVC访问和修改私有变量

    在日常开发中,对于类的私有属性,在外部定义的对象,是无法直接访问私有属性的,但是对于KVC而言,一个对象没有自己的隐私,所以可以通过KVC修改和访问任何私有属性

    3、多值操作(model和字典互转)

    //字典转模型
    - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
    
    //模型转字典
    - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
    

    4、修改一些系统控件的内部属性

    在日常开发中,我们知道,很多UI控件都是在其内部由多个UI空间组合而成,这些内部控件苹果并没有提供访问的API,但是使用KVC可以解决这个问题,常用的就是自定义tabbar、个性化UITextField中的placeHolderText

    5、用KVC实现高阶消息传递

    在对容器类使用KVC时,valueForKey:将会被传递给容器中的每一个对象,而不是对容器本身进行操作,结果会被添加到返回的容器中,这样,可以很方便的操作集合 来返回 另一个集合

    //KVC实现高阶消息传递
    - (void)transmitMsg{
        NSArray *arrStr = @[@"english", @"franch", @"chinese"];
        NSArray *arrCapStr = [arrStr valueForKey:@"capitalizedString"];
        
        for (NSString *str in arrCapStr) {
            NSLog(@"%@", str);
        }
        
        NSArray *arrCapStrLength = [arrCapStr valueForKeyPath:@"capitalizedString.length"];
        for (NSNumber *length in arrCapStrLength) {
            NSLog(@"%ld", (long)length.integerValue);
        }
    }
    
    //********打印结果********
    2020-10-27 11:33:43.377672+0800 CJLCustom[60035:6380757] English
    2020-10-27 11:33:43.377773+0800 CJLCustom[60035:6380757] Franch
    2020-10-27 11:33:43.377860+0800 CJLCustom[60035:6380757] Chinese
    2020-10-27 11:33:43.378233+0800 CJLCustom[60035:6380757] 7
    2020-10-27 11:33:43.378327+0800 CJLCustom[60035:6380757] 6
    2020-10-27 11:33:43.378417+0800 CJLCustom[60035:6380757] 7
    

    参考:https://www.jianshu.com/p/ab8c754761bf

    相关文章

      网友评论

        本文标题:iOS-KVC 底层原理

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