【iOS】KVC

文章目录


前言

KVC的全称是Key-Value Coding,翻译成中文叫做键值编码

KVC提供了一种间接访问属性方法或成员变量 的机制,允许通过字符串来访问对应的属性方法或成员变量

它是一个非正式的Protocol,提供一种机制来间接访问对象的属性,而不是通过调用Setter、Getter方法访问。KVO 就是基于 KVC 实现的关键技术之一

一、KVC常用方法

通过key 设值/取值

bash 复制代码
//直接通过Key来取值
- (nullable id)valueForKey:(NSString *)key;

//通过Key来设值
- (void)setValue:(nullable id)value forKey:(NSString *)key;

通过keyPath (即路由)设值/取值

bash 复制代码
//通过KeyPath来取值
- (nullable id)valueForKeyPath:(NSString *)keyPath; 

//通过KeyPath来设值                 
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  

其他方法

bash 复制代码
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly;

//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

//如果Key不存在,且KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (nullable id)valueForUndefinedKey:(NSString *)key;

//和上一个方法一样,但这个方法是设值。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (void)setNilValueForKey:(NSString *)key;

//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

二、key与keypath

区别

key:只能接受当前类所具有的属性,不管是自己的,还是从父类继承过来的
keypath:除了能接受当前类的属性,还能接受当前类属性的属性,即可以接受关系链,然后进行深层访问

key用法

bash 复制代码
        People *t1 = [[People alloc] init];
        Address *t2 = [[Address alloc] init];
        t1.Ad = t2;
        
        [t1 setValue:@"顶针" forKey:@"name"];
        NSLog(@"%@", [t1 valueForKey:@"name"]);

输出:

keypath用法

输出:

三、批量存值操作

同样,也可以通过KVC进行批量操作,使用对象调用setValuesForKeysWithDictionary:方法时,可以传入一个包好keyvalue的字典进去,KVC可以将所有数据按照属性名和字典的key进行匹配,并将value给对象的属性赋值

bash 复制代码
        NSDictionary *dictionarySecond = @{@"name":@"顶针", @"age":@"11", @"sex":@"女"};
        People *t3 = [[People alloc] init];
                [t3 setValuesForKeysWithDictionary:dictionarySecond];
                NSLog(@"name = %@, age = %ld, sex = %@",t3.name, (long)t3.age, t3.sex);

四、字典与模型相互转化

如果model属性和dic不匹配,可以重写方法-(void)setValue:(id)value forUndefinedKey:(NSString *)key。

这一点后面会讲到

字典转模型

bash 复制代码
NSDictionary *dictionary = @{@"name":@"stu1", @"age":@66, @"sex":@"nv"};
               StudentModel *model = [[StudentModel alloc] init];
               [model setValuesForKeysWithDictionary:dictionary];
               NSLog(@"model.name:%@",model.name);
               NSLog(@"model.age:%@",model.age);
               NSLog(@"model.sex:%@",model.studentSex);

输出:

模型转字典

bash 复制代码
NSDictionary *tempModelDictionary = [model dictionaryWithValuesForKeys:@[@"name", @"age", @"studentSex"]];
            NSLog(@"tempModelDictionary : %@", tempModelDictionary);

输出:

五、KVC底层原理

KVC设值底层原理

在日常开发中,针对对象属性的赋值,一般有以下两种方式

  • 直接通过setter方法赋值
  • 通过KVC键值编码的相关API赋值
bash 复制代码
LGPerson *person = [[LGPerson alloc] init];
// 1、一般setter 方法
person.name      = @"CJL_哈哈";
// 2、KVC方式
[person setValue:@"CJL_嘻嘻" forKey:@"name"]; 

下面针对setValue:forKey进行底层原理探索

在这里,我们通过Key-Value Coding Programming Guide苹果官方文档来研究,针对设值流程,有如下说明

当调用setValue:forKey:设置属性value时,其底层的执行流程为

  • 【第一步】首先查找是否有这三种setter方法,按照查找顺序为set<Key>:-> _set<Key> -> setIs<Key>

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

如果都没有则进入第二步

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

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

如果找到其中任意一个实例变量,则赋值,如果都没有,则进入【第三步】

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

综上所述,KVC通过 setValue:forKey: 方法设值的流程以设置LGPerson的对象person的属性name为例,如下图所示

KVC取值底层原理

当调用valueForKey:时,其底层的执行流程如下

  • 首先会按照getKey、key、isKey、_key的顺序查找方法,找到直接调用取值
  • 若未找到,则查看+ (BOOL)accessInstanceVariablesDirectly的返回值,若返回NO,则直接抛出NSUnknowKeyExpection异常;
  • 若返回的YES,则按照_key、_isKey、key、isKey的顺序查找成员变量,找到则取值;
  • 找不到则调用valueForUndefinedKey:抛出NSUnknowKeyExpection异常;
相关推荐
九丝城主9 小时前
2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--上篇
服务器·flutter·macos·vmware
芳草萋萋鹦鹉洲哦14 小时前
【vue3+tauri+rust】如何实现下载文件mac+windows
windows·macos·rust
恋猫de小郭21 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
点金石游戏出海1 天前
每周资讯 | Krafton斥资750亿日元收购日本动画公司ADK;《崩坏:星穹铁道》新版本首日登顶iOS畅销榜
游戏·ios·业界资讯·apple·崩坏星穹铁道
旷世奇才李先生1 天前
Swift 安装使用教程
开发语言·ios·swift
90后的晨仔1 天前
Xcode16报错: SDK does not contain 'libarclite' at the path '/Applicati
ios
finger244801 天前
谈一谈iOS线程管理
ios·objective-c
Digitally1 天前
如何将大型视频文件从 iPhone 传输到 PC
ios·iphone
梅名智1 天前
IOS 蓝牙连接
macos·ios·cocoa
美狐美颜sdk2 天前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk