【iOS】—— KVC再学习

news2024/10/6 10:29:43

KVC

文章目录

  • KVC
        • KVC常用的四种方法
      • key和keyPath的区别
        • 用法:
      • 批量存值操作
      • 批量赋值操作
      • 字典模型相互转化
      • KVC的其他方法
    • KVC原理探索
      • `setValue:forKey:` 的原理(KVC赋值原理)
      • `valueForKey:`的原理(KVC取值原理)
    • 注意事项

KVC的全称是KeyValueCoding,俗称“键值编码”,可以通过一个key来访问某个属性;

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

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

KVC常用的四种方法

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;

key和keyPath的区别

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

用法:

        //key的设值取值
        Person *personPath = [[Person alloc] init];
        [personPath setValue:@"I am Father" forKey:@"name"];
        NSLog(@"%@", [personPath valueForKey:@"name"]);

输出结果:
在这里插入图片描述

        //keypath的设值取值
        personPath.son = [[PersonSon alloc] init];
        [personPath setValue:@"I am Son" forKeyPath:@"son.sonName"];
        NSLog(@"%@", [personPath valueForKeyPath:@"son.sonName"]);
        NSLog(@"%@", personPath.son.sonName);

输出结果:
在这里插入图片描述

批量存值操作

KVC还有更强大的功能,可以根据给定的一组key,获取到一组value,并且以字典的形式返回;

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

批量赋值操作

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

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

示例代码:

        //逐个赋值
        Person *personFirst = [[Person alloc] init];
        [personFirst setValue:@"zxb" forKey:@"name"];
        [personFirst setValue:@20 forKey:@"age"];
        [personFirst setValue:@"男" forKey:@"sex"];
        NSLog(@"name = %@, age = %ld, sex = %@",personFirst.name, (long)personFirst.age, personFirst.sex);
        
        //通过字典赋值取值
        NSDictionary *dictionaryFirst = [personFirst dictionaryWithValuesForKeys:@[@"name", @"age", @"sex"]];
        NSLog(@"dictionaryFirst = %@", dictionaryFirst);
        
        NSDictionary *dictionarySecond = @{@"name":@"zzy", @"age":@11, @"sex":@"女"};
        Person *personSecond = [[Person alloc] init];
        [personSecond setValuesForKeysWithDictionary:dictionarySecond];
        NSLog(@"name = %@, age = %ld, sex = %@",personSecond.name, (long)personSecond.age, personSecond.sex);

输出结果:
在这里插入图片描述

字典模型相互转化

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

//StudentModel.h
@interface StudentModel : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *age;
@property (nonatomic, strong) NSString *studentSex;

@end

//StudentModel.m
@implementation StudentModel

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if ([key isEqualToString:@"sex"]) {
        self.studentSex = (NSString *)value;
    }
}

@end

//main.m
        //字典模型相互转化
        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);
        
        NSDictionary *tempModelDictionary = [model dictionaryWithValuesForKeys:@[@"name", @"age", @"studentSex"]];
        NSLog(@"tempModelDictionary : %@", tempModelDictionary);

输出结果:
在这里插入图片描述

KVC的其他方法

// 默认返回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;

// 使用字典为Model赋值
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

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

KVC原理探索

setValue:forKey: 的原理(KVC赋值原理)

  • 首先会按照setKey_setKey的顺序查找方法,找到方法,直接调用方法并赋值;
  • 未找到方法,则调用+ (BOOL)accessInstanceVariablesDirectly(是否可以直接访问成员变量,默认返回YES);
  • accessInstanceVariablesDirectly方法返回YES,则按照_key_isKeykeyisKey的顺序查找成员变量,找到直接赋值,找不到则抛出NSUnknowKeyExpection异常;
  • accessInstanceVariablesDirectly方法返回NO,那么就会调用setValue:forUndefinedKey:并抛出NSUnknowKeyExpection异常;

请添加图片描述

这里提到了setValue:forUndefinedKey方法,这个方法怎么用呢?

在 iOS 的 KVC(键-值编码)中,如果调用了一个对象的 setValue:forKey: 方法,但是这个对象并没有对应的属性或者实例变量来存储这个值,那么就会触发这个对象的 setValue:forUndefinedKey: 方法。这个方法的作用就是在运行时动态地为这个对象添加新的属性或者实例变量,并将这个值存储到新添加的属性或实例变量中。
需要注意的是,如果一个类没有实现 setValue:forUndefinedKey: 方法,那么默认会抛出一个异常。因此,如果你在使用 KVC 的时候发现程序崩溃了,并且错误信息中包含 “NSUnknownKeyException” 这个字符串,那么很可能就是因为这个原因。

valueForKey:的原理(KVC取值原理)

  • 首先会按照getKeykeyisKey_key的顺序查找方法,找到直接调用取值
  • 若未找到,则查看+ (BOOL)accessInstanceVariablesDirectly的返回值,若返回NO,则直接抛出NSUnknowKeyExpection异常;
  • 若返回的YES,则按照_key_isKeykeyisKey的顺序查找成员变量,找到则取值;
  • 找不到则调用valueForUndefinedKey:抛出NSUnknowKeyExpection异常;
    请添加图片描述

注意事项

  • key的值必须正确,如果拼写错误,会出现异常。
  • key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来。
  • 因为类可以反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去。
  • NSArrayNSSet等都支持KVC。
  • 可以通过KVC访问自定义类型的私有成员。
  • 如果对非对象传递一个nil值,KVC会调用setNIlValueForKey方法,我们可以重写这个方法来避免传递nil出现的错误,对象并不会调用这个方法,而是会直接报错。
  • 处理非对象,setValue时,如果要赋值的对象是基本类型,需要将值封装成NSNumber或者NSValue类型,valueForKey时,返回的是id类型的对象,基本数据类型也会被封装成NSNumber或者NSValuevalueForKey可以自动将值封装成对象,但是setValue:forKey:却不行。我们必须手动讲值类型转换成NSNumber/NSValue类型才能进行传递initWithBool:(BOOL)value

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/457699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++数据结构:手撕AVL树

目录 一. 什么是AVL树 二. AVL树的节点定义 三. AVL树的插入操作 3.1 寻找插入位置 3.2 更新平衡因子 3.3 AVL树的旋转调整 3.4 AVL树插入操作的整体实现 四. AVL树的检验 附录&#xff1a;AVL树的实现完整代码 AVL树定义代码 -- AVLTree.h AVL树检验代码 -- test.…

你了解PostProcessor机制吗?

Spring框架对于后置处理器的最佳实践 PostProcessor译为后置处理器&#xff0c;大多数开发人员都使用过springboot对后置处理器的实例级别实践&#xff0c;也就是BeanPostProcessor接口。其实spring还提供了两种容器级别的实践&#xff1a;BeanDefinitionRegistryPostProcesso…

今天试了试chatgpt

今天试了试chatgpt&#xff0c;真是服了 arcade&#xff1f; Arcade是一个Python游戏开发库&#xff0c;它提供了一系列的工具和函数&#xff0c;可以帮助开发者快速地创建2D游戏。以下是Arcade的一些特点&#xff1a; 简单易用&#xff1a;Arcade提供了简单易用的API&#x…

egg3.0连接egg-mongoose插入一条数据、插入多条数据

插入一条数据 app/router.js use strict;/*** param {Egg.Application} app - egg application*/ module.exports app > {const { router, controller } app;router.get(/, controller.home.index);router.get(/role, controller.role.index);router.post(/role/add, co…

【ChatGPT】如何用十分钟部署一个属于自己的chatgpt网站

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…

【两个月算法速成】day03-链表

目录 203. 移除链表元素 题目链接 思路 代码 206. 反转链表 题目链接 思路 代码 总结 203. 移除链表元素 题目链接 力扣 思路 如下图所示就是移除链表的过程 但是值得注意的是&#xff0c;移除头节点和其他位置的节点是不一样的&#xff0c;以为头结点前面没有节点。…

每日学术速递4.24

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Collaborative Diffusion for Multi-Modal Face Generation and Editing(CVPR 2023) 标题&#xff1a;多模态人脸生成和编辑的协同扩散 作者&#xff1a;Ziqi Huang, Kelvin C.K. …

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页---前后端分离)、axios加载失败)

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页—前后端分离)、axios加载失败) Vue cli CLI是Commond-Line Interface&#xff0c;翻译为命令界面&#xff0c;又称脚手架。VueCLI是一个官方发布vue.js项目脚手架。使用VueCLI可以快速搭建vue开发…

【IAR工程】STM8S基于ST标准库读取DHT11数据

【IAR工程】STM8S基于ST标准库读取DHT11数据 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取&#xff0c;请多多尊重和支持原创!&#x1f341;对于文中所提供的相关资源链接将作不定期更换。&…

HTTP协议 GET和POST区别 请求响应 Fiddler postman ajax

&#x1f496; 欢迎来阅读子豪的博客&#xff08;JavaEE篇 &#x1f934;&#xff09; &#x1f449; 有宝贵的意见或建议可以在留言区留言 &#x1f4bb; 欢迎 素质三连 点赞 关注 收藏 &#x1f9d1;‍&#x1f680;码云仓库&#xff1a;补集王子的代码仓库 不要偷走我小火…

Mac下nvm安装使用

​欢迎光临我的博客查看最新文章: https://river106.cn 1、简介 nvm 是 Mac 下的 node.js 管理工具。可以通过 nvm 安装和切换不同版本的 node.js。 官网&#xff1a;https://nvm.uihtm.com/ github&#xff1a;https://github.com/nvm-sh/nvm 2、安装 curl -o- https://raw…

移动端适配rem方案

做移动端的适配我们就是要考虑&#xff0c;对于不同大小的手机屏幕&#xff0c;怎么动态改变页面布局中所有盒子的宽度高度、字体大小等。 这个问题我们可以使用相对单位rem。 那么什么是 rem&#xff1f; rem&#xff08;font size of the root element&#xff09;是指相对…

Linux-中断和时间管理(上)

目录 中断的进入过程 中断的进入过程 为方便实验&#xff0c;本章以配套的目标板 FS4412为例来介绍 Linux 的中断子系统&#xff0c;并且编写相应的中断处理程序。FS4412 上的处理器是 SAMSUNG公司的 Exynos4412&#xff0c;该处理器使用的是4核的 Cortex-A9&#xff0c;&…

c++Lambda匿名函数

cLambda匿名函数 &#xff08;1&#xff09; 定义a. [外部变量方位方式说明符]b. (参数)c. mutabled.noexcept/throw()e.->返回值类型f.函数体 2&#xff09;c11中的拉姆达表达式中的&#xff08;&#xff09;可以省略吗 所谓匿名函数&#xff0c;简单地理解就是没有名称的函…

《C++ Primer Plus》(第6版)第17章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第17章编程练习 《C Primer Plus》&#xff08;第6版&#xff09;第17章编程练习1. 计算输入流中第一个\$之前的字符数目2. 将键盘输入&#xff08;直到模拟的文件尾&#xff09;复制到通过命令行指定的文件中3. 将一个文件复制…

完全免费的基于区块链和 IPFS 的去中心化博客平台

一、前言 xLog是一个基于Crossbell区块链的博客解决方案&#xff0c;专注于Web3数据由用户掌控。Crossbell是一个基于Web3技术的去中心化博客平台&#xff0c;用户可以在该平台上发布文章并进行交流和创作。社区提供多种交流平台和有奖创作活动。 xLog是基于 Crossbell 区块链…

【AI回复】“我问它,你对五一调休怎么看”

前言 马上就要到五一啦&#xff0c;放假打算去哪里玩呢&#xff1f; “我肯定是宅在家里写博客啊” 最近五一调休在某博上引起大家的共鸣&#xff0c;看了评论那叫一个惨不忍睹哇。 因为我比较对AI感兴趣&#xff0c;所以想看看它是怎么看待调休的。 首先&#xff0c;在百度…

【UE】简易的水材质

引擎版本&#xff1a;4.26 效果 步骤 1. 创建一个材质&#xff0c;命名为“M_Water” 2. 打开“M_Water”&#xff0c;将混合模式设为半透明&#xff0c; 光照模式设为表面半透明体积&#xff0c;在这种模式下我们可以使用金属度、粗糙度等接口 3. 创建一个4维常量节点&…

Android 基于NumberPicker自定义弹出窗口Dialog整合日期选择器

Android实现把年月选择器放到AlertDialog中_左眼看成爱的博客-CSDN博客 Android使用NumberPicker实现年月滚动选择器_左眼看成爱的博客-CSDN博客 前面两篇文章我们分别讲了 1&#xff0c;如何用NumberPicker实现年月选择器 2&#xff0c;如何把1中的用NumberPicker实现的年…

基于DE2-115平台实现VGA显示器的显示实验

目录 什么是VGA协议VGA显示原理VGA时序图VGA参数图实验记录准备PLLROM取模代码data_drive.vkey_debounce.vvga_drive.vvga_top.v 实验现象 什么是VGA协议 这一部分摘录自野火的征途Pro《FPGA Verilog开发实战指南——基于Altera EP4CE10》2021.7.10&#xff08;上&#xff09;…