【iOS开发】—— KVC

news2024/9/23 9:34:11

【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;

@end

NS_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.coloradmin.cn/o/1698603.html

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

相关文章

3D牙科网格分割使用基于语义的特征学习与图变换器

文章目录 3D Dental Mesh Segmentation Using Semantics-Based Feature Learning with Graph-Transformer摘要方法实验结果 3D Dental Mesh Segmentation Using Semantics-Based Feature Learning with Graph-Transformer 摘要 本文提出了一种新颖的基于语义的牙科网格分割方…

小程序怎么改名

经常有商家想要对自己的小程序进行重命名&#xff0c;改名可能是为了更好地与品牌形象以及业务相匹配&#xff0c;也可能是为了更好地吸引用户。那么如何才能更名呢&#xff1f; 一、准备几个新名字。 在决定改名之前&#xff0c;首先要确定几个新的小程序名字。为什么要准备…

CCF20231201——仓库规划

CCF20231201——仓库规划 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; int main() {int n,m,a[1001][11],b[1001]{0};cin>>n>>m;for(int i1;i<n;i){for(int j1;j<m;j)cin>>a[i][j];}for(int i1;i<n;i){bool foundfals…

Excel必知必会

文章目录 基础概念数据格式选择区域内指定格式数据多行筛选数据转换数据格式固定首行和首列在滚动时一直显示指定列数据符合预期批量填充公式 函数VLOOKUP函数 基础概念 数据格式 文本&#xff0c;数值&#xff08;默认值0&#xff09;&#xff0c;&#xff08;逻辑值&#x…

ciscn2024(上传一下,有侵权什么的问题的话联系删除)

Web Simple_php 这个Simple_php一点儿也不Simple (⋟﹏⋞) 源码放这儿了&#xff1a; <?phpini_set(open_basedir, /var/www/html/); error_reporting(0);if(isset($_POST[cmd])){$cmd escapeshellcmd($_POST[cmd]); if (!preg_match(/ls|dir|nl|nc|cat|tail|more|flag…

部署ELK日志分析系统——超详细

ELK日志分析系统 文章目录 ELK日志分析系统资源列表基础环境一、环境准备二、部署Elasticsearch软件2.1、安装Elasticsearch软件2.2、加载系统服务2.3、更改Elasticsearch主配置文件2.4、创建数据存放路径并授权2.5、启动Elasticsearch2.6、查看节点信息 三、安装Elasticsearch…

如何使用GPT-4o?

GPT-4o的时代已经到来&#xff01;昨天分享群有小伙伴询问怎么使用GPT-4o&#xff1f; 在接下来的几周&#xff0c;OpenAI将陆续向所有用户开放GPT-4o的访问权限。无论是ChatGPT Free、Plus还是Team的用户&#xff0c;都能享受到GPT-4o带来的创新体验&#xff08;企业版也已经…

Go语言之GORM框架(二) ——GORM的单表操作

前言 在上一篇文章中&#xff0c;我们对Gorm进行了介绍&#xff0c;而在这一篇文章中我们主要介绍GORM的单表查询与Hook函数,在进行今天的内容之前我们先事先说明一下&#xff0c;下面我们对单表进行操作的表结构如下&#xff1a; type Student struct {ID uint gorm:&qu…

贪心-AcWing 1522. 排成最小的数字-XMUOJ石板序列

题目 思路 getline() 是 C 标准库中的一个函数&#xff0c;用于从输入流中读取一行文本&#xff0c;并将其存储为字符串。它可以从标准输入、文件流、字符串流等不同类型的输入流中读取数据。C中istringstream、ostringstream、stringstream详细介绍和使用_c istringstream-CS…

智能合约语言(eDSL)—— 并行化方案 2

这个并行算法最初其实是在aptos上实现的&#xff0c;aptos上使用的是move虚拟机&#xff0c;后来我把它移植到我们链上了&#xff0c;但是wasm虚拟机。还是费了不少事情。 目前evm并行也比较火&#xff0c;像monad&#xff0c;sei等。经过调研发现&#xff0c;其实evm的并行&am…

代码随想录|Day42|动态规划 part07|● 70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 322. 零钱兑换 class Solution: def climbStairs(self, n: int) -> int: if n < 1: return n dp [0] * (n 1) dp[0] 0 dp[1] 1 dp[2] 2 for i in range(3, n 1): dp[i] dp[i - 1] dp[i - 2] return dp[n] 279.完全平方数…

spring状态机实战

一、什么是状态机 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型&#xff0c;是一种概念性机器&#xff0c;它能采取某种操作来响应一个外部事件。这种操作不仅能取决于接收到的事件&#xff0c;还能取决于各个事件的相对发生顺序。状态…

Mybase长久破解

1、软件下载好之后&#xff0c;找到文件mybase8.ini文件 2、使用记事本打开&#xff0c;通过 Ctrl F 输入快速找到属性设置FirstUseOn.UserLic.App&#xff0c;将等号后面的数值删掉保存即可 3、使用防护中心–>自定义防护&#xff08;记得开启&#xff09; 4、添加规则…

深入JVM元空间以及弹性伸缩机制

个人博客 深入JVM元空间以及弹性伸缩机制 | iwts’s blog JVM内存模型中元空间所在位置 即在JVM运行时的内存模型。总体上有这样的图&#xff1a; 元空间 上面的图其实有点不太准。方法区本质上只是JVM的一个标准&#xff0c;不同JVM在不同版本下都可能有不同的实现&#x…

Java进阶学习笔记28——StringJoiner

Java中&#xff0c;有没有即能高效&#xff0c;又能实现更方便的拼接呢&#xff1f; StringJoiner&#xff1a; JDK8才开始的&#xff0c;跟StringBuilder一样&#xff0c;也是用来操作字符串的&#xff0c;也可以看成是一个容器&#xff0c;创建之后里面的内容是可变的。 好…

动态路由实验—OSPF

动态路由协议实验-------OSPF 链路状态路由选择协议又被称为最短路径优先协议&#xff0c;它基SPF&#xff08;shortest path first &#xff09;算法 实验要求&#xff1a;各个PC之间能够互通 1.四台PC配置如下 PC1 PC2 PC3 PC4 2.配置各个交换机的口子的IP R1 <HUAWE…

文件批量重命名利器:一键轻松替换文本间内容,高效管理文件不再是难题!

在信息爆炸的时代&#xff0c;我们的电脑中堆积了无数的文件。这些文件可能包含重要的工作资料、珍贵的个人回忆或是各种学习资料。然而&#xff0c;随着文件的不断增多&#xff0c;如何高效地管理和查找这些文件成为了一个头疼的问题。 文件批量改名高手是一款专业的文件管理…

轮廓系数(Average silhouette) | 最佳聚类数的判定

1.最佳分类个数 # 辅助确定最佳聚类数 4.7*2.6 factoextra::fviz_nbclust( t(DPAU_2), kmeans, method "silhouette")在2有下降拐点&#xff0c;但是样本较多时分成2类一般意义不大。 在7时也有下降拐点。 2.查看每个分类的轮廓系数 (1) pam k5 library(cluste…

基于地理坐标的高阶几何编辑工具算法(7)——矩形绘制

文章目录 工具步骤应用场景示意图算法原理工具步骤 点击矩形绘制工具,点击三个点完成矩形绘制。 应用场景 用于在地图上快速绘制任意方向的矩形。 示意图 算法原理 点第一个点确定矩形的一个角点P1,也作为平移后的坐标原点,生成平移矩阵。点第二个点P2,确定矩形的一条边…