【iOS开发】—— KVC

【iOS开发】—— KVC

  • 一. KVC的定义
    • key和keyPath的区别
      • 用法:
    • 批量复制操作
    • 字典模型相互转化
    • KVC的其他方法
  • KVC原理
    • 赋值原理
    • 取值原理

一. KVC的定义

KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。很多高级的iOS开发技巧都是基于KVC实现的。

KVC的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的,因为没有继承NSObject),下面是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* person = [[Person alloc] init];
[person setValue:@"I am Father" forKey:@"name"];
NSLog(@"%@", [person valueForKey:@"name"]);

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

person.son = [[PersonSon alloc] init];
[person setValue:@"I am Son" forKeyPath:@"son.sonName"];
NSLog(@"%@", [person.son valueForKey:@"sonName"]);

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

批量复制操作

Person* personFirst = [[Person alloc] init];
[personFirst setValue:@"lcy" forKey:@"name"];
[personFirst setValue:@"20" forKey:@"age"];
[personFirst setValue:@"男" forKey:@"sex"];
NSLog(@"name = %@, age = %ld, sex = %@",personFirst.name, (long)personFirst.age, personFirst.sex);NSDictionary* dictionary1 = [personFirst dictionaryWithValuesForKeys:@[@"name", @"age", @"sex"]];
NSLog(@"dictionary1 = %@", dictionary1);NSDictionary* dicioinary2 = @{@"name": @"lyt", @"age": @11, @"sex": @"男"};
Person* personSecond = [[Person alloc] init];
[personSecond setValuesForKeysWithDictionary:dictioinary2];
NSLog(@"name = %@, age = %ld, sex = %@",personSecond.name, (long)personSecond.age, personSecond.sex);

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

字典模型相互转化

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

重点:-(void)setValue:(id)value forUndefinedKey:(NSString *)key 方法在函数中有定义,但是没有实现需要自己来实现,从而供后面来调用。如果自己不重写的话,遇到Key不存在,且KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface StudentModel : NSObject@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSString* age;
@property (nonatomic, strong) NSString* studentSex;@endNS_ASSUME_NONNULL_END#import "StudentModel.h"@implementation StudentModel
- (void) setValue:(id)value forUndefinedKey:(NSString *)key {if ([key isEqualToString:@"sex"]) {self.studentSex = (NSString*) value;}
}
@end
//main函数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* tempdict = [model dictionaryWithValuesForKeys:@[@"name", @"age", @"studentSex"]];
NSLog(@"tempdict = %@", tempdict);

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

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原理

赋值原理

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

  • 直接通过setter方法赋值;
  • 通过KVC键值编码的相关API赋值;

下面针对使用最多的KVC设值方法:setValue:forKey,来进行其底层原理的探索。

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

  1. 【第一步】首先查找是否有这三种setter方法,按照查找顺序为set:-> _set -> setIs
    • 如果有其中任意一个setter方法,则直接设置属性的value(主注意:key是指成员变量名,首字符大小写需要符合KVC的命名规范)
    • 如果都没有,则进入【第二步】
  2. 【第二步】:如果没有第一步中的三个简单的setter方法,则查找accessInstanceVariablesDirectly是否返回YES,
    • 如果返回YES,则查找间接访问的实例变量进行赋值,查找顺序为:_ -> _is -> -> is
      • 如果找到其中任意一个实例变量,则赋值。
      • 如果都没有,则进入【第三步】
    • 如果返回NO,则进入【第三步】
  3. 【第三步】如果setter方法 或者 实例变量都没有找到,系统会执行该对象的setValue:forUndefinedKey:方法,默认抛出NSUndefinedKeyException类型的异常
    综上所述,KVC通过 setValue:forKey: 方法设值的流程以设置LGPerson的对象person的属性name为例,如下图所示:
    在这里插入图片描述

取值原理

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

  1. 【第一步】首先查找getter方法,按照get< Key> -> < key> -> is< Key> -> _< key> 的方法顺序查找,
  • 如果找到,则进入【第五步】。
  • 如果没有找到,则进入【第二步】。
  1. 【第二步】如果第一步中的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的标准命名方法,包括方法签名。)
  • 如果没有找到这三个访问数组的,请继续进入【第三步】。
  1. 【第三步】如果没有找到上面的几种方法,则会同时查找 countOf < Key>,enumeratorOf< Key>和memberOf< Key> 这三个方法
  • 如果这三个方法都找到,则会创建一个响应所有NSSet方法的集合代理对象,并返回该对象,此代理对象随后将其收到的所有NSSet消息转换为countOf< Key>,enumeratorOf< Key>和memberOf< Key>:消息的某种组合,用于创建它的对象

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

  1. 【第四步】如果还没有找到,检查类方法InstanceVariablesDirectly是否YES,依次搜索 _< key>,_is< Key>,< key>或is< Key> 的实例变量。
  • 如果搜到,直接获取实例变量的值,进入【第五步】
  1. 【第五步】根据搜索到的属性值的类型,返回不同的结果
  • 如果是对象指针,则直接返回结果。
  • 如果是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回它。
  • 如果是是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。
  1. 【第六步】如果上面5步的方法均失败,系统会执行该对象的valueForUndefinedKey:方法,默认抛出NSUndefinedKeyException类型的异常。

综上所述,KVC通过 valueForKey: 方法取值的流程以设置LGPerson的对象person的属性name为例,如下图所示:
在这里插入图片描述

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

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

相关文章

不用从头训练,通过知识融合创建强大的统一模型

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;的开发和训练是一个复杂且成本高昂的过程。数据需求是一个主要问题&#xff0c;因为训练这些模型需要大量的标注数据来保证其准确性和泛化能力&#xff1b;计算资源也是一个…

Java学习路线思维导图

目录 Java学习流程1.学习大纲2.Java开发中常用的DOS命令 Java入门学习思维导图 Java学习流程 通过大纲了解学习的重点&#xff0c;通过目录依次深入【注&#xff1a;Java环境的搭建百度&#xff0c;提升自己百度的能力】 1.学习大纲 学习流程如下&#xff1a; Java基础语法 …

网络安全架构之零信任安全

网络安全架构之零信任安全 文章目录 网络安全架构之零信任安全零信任安全时代背景安全世界“新旧时代”各种攻击风险层出不穷网络安全边界逐渐瓦解内外部威胁愈演愈烈 零信任架构零信任的理念在不可信的网络环境下重建信任构建自适应内生安全机制以身份为基石业务安全访问持续信…

Linux服务的简介与分类

服务的简介与分类 服务的分类 查询已安装的服务和区分服务 #列出所有rpm包默认安装服务的自启动状态 [rootlocalhost ~]# chkconfig --list atd atd 0:关闭 1:关闭 2:关闭 3:启用 4:启用 5:启用 6:关闭 [rootlocalhost ~]# chkconfig --list sshd sshd …

SpringBoot项目中访问HTML页面

在这种情况下&#xff0c;如果你要访问静态页面&#xff0c;肯定是不能正确访问的&#xff1a;会出现如下错误&#xff1a; 那么&#xff0c;此时&#xff0c;你应该&#xff1a; 静态资源映射&#xff1a; import org.springframework.context.annotation.Configuration; im…

command not found: wire 解决方案【学习笔记,不作教程】

command not found: wire command not found: wire command not found: wire go get github.com/google/wire/cmd/wirego install github.com/google/wire/cmd/wirelatest再次在 /bubble/cmd/bubble目录下执行wire wire wire: bubble/cmd/bubble: wrote /Users/zhengshijie/go…

音视频开发5 补充 - Nginx搭建rtmp流媒体服务器,目的是让ffmpeg 可以直播推流

直播推流 ffmpeg -re -i out.mp4 -c copy flv rtmp://server/live/streamName -re, 表示按时间戳读取文件 参考&#xff1a; Nginx 搭建 rtmp 流媒体服务器 (Ubuntu 16.04) https://www.jianshu.com/p/16741e363a77 第一步 准备工作 安装nginx需要的依赖包 打开 ubutun 终端…

[emailprotected](9)属性默认值和类型验证

目录 1&#xff0c;属性默认值1.1&#xff0c;函数组件1.2&#xff0c;类组件 2&#xff0c;属性类型验证2.1&#xff0c;和默认值的关系2.2&#xff0c;使用2.3&#xff0c;举例说明 1&#xff0c;属性默认值 通过组件的 defaultProps 静态属性设置。 1.1&#xff0c;函数组…

lllllllllll

ollllllllllllll

Leetcode 3160. Find the Number of Distinct Colors Among the Balls

Leetcode 3160. Find the Number of Distinct Colors Among the Balls 1. 解题思路2. 代码实现 题目链接&#xff1a;3160. Find the Number of Distinct Colors Among the Balls 1. 解题思路 这一题思路上同样比较清晰&#xff0c;我们只需用两个hash table来分别记录每一个…

Nginx与CDN集成:提升全球访问速度

一、引言 在当今互联网高速发展的时代&#xff0c;用户对于网站访问速度的要求越来越高。为了提升用户体验&#xff0c;许多企业和开发者都在寻求各种方法来优化网站的访问速度。其中&#xff0c;Nginx与CDN&#xff08;内容分发网络&#xff09;的集成就是一个非常有效的方法…

社会网络,生态网络,贸易网络,复杂网络边介数蓄意和随机攻击

​边介数&#xff08;Edge Betweenness&#xff09; # ” 边介数&#xff08;Edge Betweenness&#xff09; 1 边介数&#xff08;Edge Betweenness&#xff09; Summer IS HERE 边介数&#xff08;Edge Betweenness&#xff09;是一种度量边在网络中重要性的指标。它定义为…

golang float转string并去除末尾的0

写go时遇到一个场景要求将得到的浮点数保留两位小数&#xff0c;同时要去除末尾的0&#xff0c;试了一下 fmt.Sprintf 和 strconv.FormatFloat 都没能一步到位&#xff0c;最后只能先按小数位约分然后再转成字符串来解决这个问题&#xff0c;如果各位有更好的方式请在评论里指教…

Linux dig 命令

dig 命令是一个用于在 Unix/Linux 操作系统中执行 DNS 查询的工具。它是 DNS 客户端&#xff0c;通常用于查询 DNS 服务器的信息&#xff0c;如域名解析、IP 地址查询等。 博主博客 https://blog.uso6.comhttps://blog.csdn.net/dxk539687357 一、常见 DNS 记录类型 类型描述…

OpenStack平台Nova管理

1. 规划节点 使用OpenStack平台节点规划 IP主机名节点192.168.100.10controller控制节点192.168.100.20compute计算节点 2. 基础准备 部署的OpenStack平台 1. Nova运维命令 &#xff08;1&#xff09;Nova管理安全组规划 安全组&#xff08;security group&#xff09;是…

设计模式八股文

什么是设计模式&#xff1f; 设计模式是软件开发过程中经常遇到的问题的通用解决方案。类似于前人总结的经验&#xff0c;遇到相似问题的时候有个参考。 设计模式七大基本原则&#xff1f; 单一职责&#xff1a;一个类应该只作一件事情。将功能分为小的独立的单元。开放封闭…

CTF之Web_python_block_chain

这种题对于我来说只能看大佬的wp&#xff08;但是这一题是wp都看不懂&#xff0c;只能表达一下我的理解了&#xff09; &#xff08;最后有简单方法&#xff0c;前面一种没看懂没关系&#xff09; 下面这一部分是首页的有用部分 访问/source_code,得到源码&#xff1a; # -*-…

qt post请求 c++ 解压缩文件

目录 qt post请求 c下载代码&#xff1a; 默认下载保存路径&#xff1a; linux qt调用 unzip进行解压缩 pro文件配置&#xff1a; QT network qt post请求 c下载代码&#xff1a; #include "mainwindow.h" #include "ui_mainwindow.h" #include &…

ffmpeg怎么将vtt文件添加到视频中

FFmpeg 是一个非常强大的多媒体框架&#xff0c;可以用来处理音频、视频以及字幕文件。要将 WebVTT&#xff08;.vtt&#xff09;字幕文件添加到视频文件中&#xff0c;你可以使用 ffmpeg 的 subtitles 过滤器。以下是基本的命令格式&#xff1a; ffmpeg -i input_video.mp4 -…

VSCode搭建Markdown编辑环境

1. 本文目标2. 准备工作3. 安装插件4. 插件的用法 4.1. Markdown All in One 4.1.1. 快捷键4.1.2. 创建/更新目录4.1.3. 给标题增加序号 4.2. Markdown Table 4.2.1. 格式化表格4.2.2. 插入表格列 4.3. Markdown PDF VSCode是笔者用过的最好用的开发工具&#xff0c;没有之一…