「iOS」——KVC

iOS学习

  • 前言
  • KVC模式
    • KVC设值
    • KVC取值
    • KVC使用keyPath
    • KVC处理异常
      • 处理不存在的key
      • 处理nil异常
    • KVC处理字典
    • KVC高阶消息传递
  • 总结

前言

对KVC模式的简单学习和总结。


KVC模式

KVC(Key-Value Coding,键值编码)是一种通过字符串来访问对象属性的机制,允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。可以在运行时动态地访问和修改对象的属性。而不是在编译时确定。

以下是KVC的常用方法:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;//通过keyPath设置值
- (void)setValue:(id)value forKey:(NSString *)key;//通过key设置值
- (id)valueForKeyPath:(NSString *)keyPath;//通过keyPath获取值
- (id)valueForKey:(NSString *)key;//通过key获取值

KVC设值

我们通过- (void)setValue:(id)value forKey:(NSString *)key;方法来为KVC设值,下面给出代码演示:

#import <Foundation/Foundation.h>@interface AUser : NSObject@property (nonatomic, copy) NSString *str1;
@property (nonatomic, copy) NSString *str2;@end#import <Foundation/Foundation.h>
#import "AUser.h"
#import "AUser1.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser *user = [[AUser alloc] init];[user setValue:@"Astr1" forKey:@"str1"];[user setValue:@"Astr2" forKey:@"str2"];NSLog(@"str1:%@",[user valueForKey:@"str1"]);NSLog(@"str2:%@",[user valueForKey:@"str2"]);}return 0;
}

运行结果为:
请添加图片描述
那么KVC设置的逻辑原理是什么呢?

请添加图片描述

如上图所示:

  1. 首先会按照setKey、_setKey的顺序查找方法,如找到方法,则直接调用方法并赋值;
  2. 未找到方法,则调用+ (BOOL)accessInstanceVariablesDirectly(是否可以直接访问成员变量,默认返回YES);
  3. 若accessInstanceVariablesDirectly方法返回YES,则按照_key、_isKey、key、isKey的顺序查找成员变量,找到直接赋值,找不到则抛出NSUnknowKeyExpection异常;
  4. 若accessInstanceVariablesDirectly方法返回NO,那么就会调用setValue:forUndefinedKey:并抛出NSUnknowKeyExpection异常;

我们来验证一些处理逻辑:

#import <Foundation/Foundation.h>@interface AUser1 : NSObject{@packageNSString *name;NSString *_name;
}
@end#import <Foundation/Foundation.h>
#import "AUser.h"
#import "AUser1.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser1 *aUser = [[AUser1 alloc] init];[aUser setValue:@"strName1" forKey:@"_name"];[aUser setValue:@"strName2" forKey:@"name"];NSLog(@"name = %@", aUser->name);NSLog(@"_name = %@", aUser->_name);}return 0;
}

请添加图片描述

KVC取值

我们通过- (id)valueForKey:(NSString *)key; 方法来获取值。
下面来探究取值顺序:

#import "AUser.h"@implementation AUser-(int) getAge{return 9999;
}-(int) age
{return 999;
}-(int) isAge
{return 99;
}
@end#import <Foundation/Foundation.h>
#import "AUser.h"
#import "AUser1.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser *user = [[AUser alloc] init];[user setValue:@"9" forKey:@"age"];NSLog(@"age:%@",[user valueForKey:@"age"]);}return 0;
}

运行后:

请添加图片描述
注释掉getAge后:

请添加图片描述
注释掉age方法后:
请添加图片描述
再注释掉isAge方法后:
请添加图片描述

原理:

  1. 首先会按照getKey、key、isKey、_key的顺序查找方法,找到直接调用取值
  2. 若未找到,则查看+ (BOOL)accessInstanceVariablesDirectly的返回值,若返回NO,则直接抛出NSUnknowKeyExpection异常;
  3. 若返回的YES,则按照_ key、_isKey、key、isKey的顺序查找成员变量,找到则取值;
  4. 找不到则调用valueForUndefinedKey:抛出NSUnknowKeyExpection异常;

请添加图片描述

KVC使用keyPath

面对复杂的嵌套属性进行初始化赋值时,如果使用key一层层赋值十分麻烦。我们可以采用keyPath来访问对象的嵌套属性。
keyPath的两个方法:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;//通过keyPath设置值
- (id)valueForKeyPath:(NSString *)keyPath;//通过keyPath获取值

下面我们来以代码举例:
我们先创建一个BUser:

#import <Foundation/Foundation.h>@interface BUser : NSObject
@property (nonatomic, copy) NSString *strb1;
@property (nonatomic, copy) NSString *strb2;
@end

我们再创建一个AUser嵌套一个BUser:

#import <Foundation/Foundation.h>
#import "BUser.h"@interface AUser : NSObject
{BUser *bUser;
}
@end

此时,AUser对象中,含有一个嵌套属性,我们可以使用keyPath进行为嵌套对象赋值并且取值:

#import <Foundation/Foundation.h>
#import "AUser.h"
#import "BUser.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser *aUser = [[AUser alloc] init];BUser *bUser = [[BUser alloc] init];[aUser setValue:bUser forKey:@"bUser"];[aUser setValue:@"b1" forKeyPath:@"bUser.strb1"];[aUser setValue:@"b2" forKeyPath:@"bUser.strb2"];NSLog(@"b1:%@",[aUser valueForKeyPath:@"bUser.strb1"]);NSLog(@"b1:%@",[aUser valueForKeyPath:@"bUser.strb2"]);}return 0;
}

打印结果:
请添加图片描述

KVC处理异常

处理不存在的key

在上面我们说过KVC设置的顺序。如果最后没有找到相应的成员变量,则会调用setValue:forUndefinedKey:并抛出NSUnknowKeyExpection异常来结束程序。
我们只需要重写- (void) setValue: (id)value forUndefinedKey:(nonnull NSString *)key 这个方法,则不会产生Crash。


#import "AUser1.h"
@implementation AUser1{int age;
}- (void) setValue: (id)value forUndefinedKey:(nonnull NSString *)key {NSLog(@"重写了setValue:value forUndefinedKey方法");
}
@end#import <Foundation/Foundation.h>
#import "AUser1.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser1 *aUser = [[AUser1 alloc] init];//处理不存在的key[aUser setValue:@"strName1" forKey:@"1"];}return 0;
}

运行结果:
请添加图片描述

处理nil异常

我们可以将nil赋值给字符串类型,但是不能复制给int或NSInteger类型。如果需要为对象赋nil时,则需要自己处理一下nil异常的部分,例如给int类型赋nil的情况。
当我们给int类型赋值nil时,就会出现异常,会执行-(void)setNilValueForKey:(NSString *)key这个方法,使程序崩溃,所以我们通常需要重写这个方法来处理nil异常。

#import "AUser1.h"@implementation AUser1{int age;
}-(void) setNilValueForKey:(NSString *)key
{if ([key isEqualToString:@"age"]) {age = 0;} else {[super setNilValueForKey:key];}
}
@end#import <Foundation/Foundation.h>
#import "AUser1.h"int main(int argc, const char * argv[]) {@autoreleasepool {AUser1 *aUser = [[AUser1 alloc] init];[aUser setValue:nil forKey:@"age"];NSLog(@"age = %@", [aUser valueForKey:@"age"]);}return 0;
}

运行结果:

请添加图片描述

KVC处理字典

我们可以通过字典进行批量的设值取值操作。

  • setValuesForKeysWithDictionary: 方法用于将字典中的值赋给对象的属性,
  • dictionaryWithValuesForKeys: 方法则用于根据属性键数组获取对象的属性值并返回对应的字典。
#import <Foundation/Foundation.h>@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) NSInteger age;@end#import <Foundation/Foundation.h>
#import "Person.h"int main(int argc, const char * argv[]) {@autoreleasepool {Person* person = [[Person alloc] init];//使用KVC批量存值[person setValue:@"Kobe" forKey:@"name"];[person setValue:@"man" forKey:@"sex"];[person setValue:@"24" forKey:@"age"];NSDictionary* firstDictionary = [person dictionaryWithValuesForKeys:@[@"name", @"sex", @"age"]];NSLog(@"dictonary = %@", firstDictionary);//使用KVC批量赋值NSDictionary* secondDictionary = @{@"name":@"瑞娜", @"age":@2, @"sex": @"woman"};Person* secondPerson = [[Person alloc] init];[secondPerson setValuesForKeysWithDictionary:secondDictionary];NSLog(@"name  = %@, age = %ld, sex = %@", secondPerson.name, secondPerson.age, secondPerson.sex);}return 0;
}

KVC高阶消息传递

通俗来讲就是让数组中的每一个元素都执行某个方法,并把结果返回到新的数组中。这里我们实现将数组中的每个首字母大写,并且返回长度。

#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {NSArray* arrStr = @[@"reyna",@"jett",@"Neon"];NSArray* arrCapStr = [arrStr valueForKey:@"capitalizedString"];for (NSString* str  in arrCapStr) {NSLog(@"%@",str);}NSArray* arrCapStrLength = [arrStr valueForKeyPath:@"capitalizedString.length"];for (NSNumber* length  in arrCapStrLength) {NSLog(@"%ld",(long)length.integerValue);}}return 0;
}

运行结果:

请添加图片描述


总结

KVC相比于setter和getter方法,虽然在性能上差一点,但是在编码上更加的灵活,简洁,可以批量操作并且可以在运行时动态地访问和操作对象的属性。

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

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

相关文章

双端之Nginx+Php结合PostgreSQL搭建Wordpress

第一台虚拟机:安装 Nginx 更新系统包列表: sudo apt update安装 Nginx及php扩展: sudo apt install nginx php-fpm php-pgsql php-mysqli -y启动 Nginx 服务: sudo systemctl start nginx检查 Nginx 是否正常运行: xdg-open http://localhost注意:终端命令打开网址 …

腾讯云SDK产品功能

本文主要介绍音视频终端 SDK&#xff08;腾讯云视立方&#xff09;的核心功能。 直播推流 音视频终端 SDK&#xff08;腾讯云视立方&#xff09;为终端直播场景提供强大的 RTMP、RTC 推流能力&#xff0c;配合云直播&#xff08;CSS&#xff09;全球布局的2000节点&#xff0…

数据结构及基本算法

目录 第一章 概论 第一节 引言 第二节 基本概念和常用术语 第三节 算法的描述与分析 第二章 线性表 第一节 线性表定义和基本运算个 一、线性表的逻辑定义 二、线性表的基本运算 第二节 线性表的顺序存储和基本运算的实现 一、线性表的顺序存储 二、顺序表上基本运算…

【网络安全】-访问控制-burp(1~6)

文章目录 前言   1.Lab: Unprotected admin functionality  2.Lab: Unprotected admin functionality with unpredictable URL   3.Lab: User role controlled by request parameter   4.Lab:User role can be modified in user profile  5.Lab: User ID controlled by…

山海优选电商平台卷轴模式订单系统核心架构解析

山海优选卷轴模式的订单核心源码是涉及订单处理、支付、搜索、状态管理等关键功能的代码部分。由于直接提供完整的源代码可能涉及版权和隐私保护问题&#xff0c;我将基于参考文章中的信息&#xff0c;概述该模式订单核心源码的主要结构和功能点。 一、订单核心源码概述 在山海…

C语言线程

线程 多个进程中通过轮流使用CPU来完成自己的任务&#xff0c;如果多个进程的操作都一模一样那么CPU的开销就会很大&#xff0c;因为进程的地址都是私有的&#xff0c;如果CPU对相同的操作只执行一次&#xff0c;后面再遇到直接去获取即可&#xff0c;这样大大降低了CPU的开销…

【AI战略思考5】工欲善其事,必先利其器。我的利器是什么?

目录 导言1.不要忽视时间本身复利的巨大威力2.只赚自己认知以内的钱&#xff0c;只把握自己能力以内的机会3.多做有难度的事来激发自己的潜力和提升自己4.学会抵制诱惑5.减少冗余思考和冗余操作 导言 工欲善其事&#xff0c;必先利其器。我的利器是什么&#xff1f; 虽然我中考…

【C++拓展(四)】秋招建议与心得

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C拓展 1. 前言2. 今年秋招形势到底如何?3. 学历…

Proteus如何添加数码管

1、打开安装好的Proteus&#xff0c;点击上方菜单栏中的“库”&#xff0c;再选择“从库选取零件”&#xff0c;或者在左侧元件列表中单击鼠标右键&#xff0c;再点击右键菜单中的“从库中挑选”选项。 2、之后在元器件库中&#xff0c;点击类别中的“Optoelectronics”&#…

景联文科技精准数据标注:优化智能标注平台,打造智能未来

景联文科技是一家致力于为人工智能提供全面数据标注解决方案的专业公司。 拥有一支由经验丰富的数据标注师和垂直领域专家组成的团队&#xff0c;确保数据标注的质量和专业性。 自建平台功能一站式服务平台&#xff0c;提供从数据上传、标注、审核到导出的一站式服务&#xff0…

熔断降级 请求合并 请求缓存 线程池隔离 信号量隔离 openfeign整合Hystrix

〇、Hystrix的解决策略和预防措施&#xff1a; 熔断降级是解决远程调用已经出现问题的解决方案。 请求合并 请求缓存 线程池隔离 信号量隔离都是预防性措施 使用Hystrix启动类上需要添加注解开启注解支持&#xff1a; EnableHystrixEnableCircuitBreaker 测试Hystrix需要的依赖…

DAY81服务攻防-开发框架安全SpringBootStruts2LaravelThinkPHPCVE 复现

知识点&#xff1a; 1、PHP-框架安全-Thinkphp&Laravel 2、J2EE-框架安全-SpringBoot&Struts2 常见语言开发框架&#xff1a; PHP&#xff1a;Thinkphp Laravel YII CodeIgniter CakePHP Zend等 JAVA&#xff1a;Spring MyBatis Hibernate Struts2 Springboot等 P…

安宝特案例 | 某知名日系汽车制造厂,借助AR实现智慧化转型

案例介绍 在全球制造业加速数字化的背景下&#xff0c;工厂的生产管理与设备维护效率愈发重要。 某知名日系汽车制造厂当前面临着设备的实时监控、故障维护&#xff0c;以及跨地域的管理协作等挑战&#xff0c;由于场地分散和突发状况的不可预知性&#xff0c;传统方式已无法…

服务器数据恢复—存储映射到服务器上的卷无法挂载的数据恢复案例

服务器存储数据恢复环境&故障&#xff1a; 一台存储上有一组由16块FC硬盘组建了一组raid。存储前面板上的对应10号和13号硬盘的故障灯亮起&#xff0c;存储映射到redhat linux操作系统服务器上的卷挂载不上&#xff0c;业务中断。 服务器存储数据恢复过程&#xff1a; 1、…

18.Linux-配置DNF仓库

DNF仓库产生背景 在现实的场景中&#xff0c;我们经常要安装一些软件包&#xff0c;但由于现场不提供网络。 需要使用光盘或文件下载的方式去安装。 对于linux有两种离线安装方式&#xff1a;二进制文件安装和源码安装 其中二进制文件是比较简单的安装方式&#xff0c;不同的l…

C++学习9.27

1、顺序表、栈、队列都更改成模板类 &#xff08;1&#xff09;顺序表 #include <iostream> #include <cstring>using namespace std;template <typename T1,typename T2,typename T3> class My_string { private:T1 *ptr; //指向字符数组的指针T2…

iwebsec靶场 反序列化关卡通关笔记2-反序列化漏洞示例02

目录 第02关 反序列化漏洞示例02 1.打开靶场 2.源码分析 3.login函数利用 4.show函数利用 5.参数反序列化设计 6.show函数查询orange 7.增加注释语句 8.show函数SQL注入获取密码 &#xff08;1&#xff09;构造SQL语句 &#xff08;2&#xff09;构造序列化 &#…

OJ在线评测系统 后端判题机架构搭建 使用原生实现Java安全管理器环境隔离

原生实现安全管理器环境隔离 限制用户的操作权限 文件 网络 执行 Java安全管理器 SecurityManager 来实现更严格的限制 是 Java 提供的保护 JVM Java安全的机制 可以实现更严格的资源和操作限制 编写安全管理器 只需要继承 SecurityManager类 我们可以从这个参数perm参数拿…

Study--Oracle-09--部署Openfiler存储服务器

一、安装Oracle RAC需要存储&#xff0c;为此搭建安装openfiler用于模拟存储。 openfiler相关镜像包可从官网下载&#xff1a;Downloads | Openfiler 当前最新版本如下&#xff08;该笔记也是基于如下版本&#xff09; 二、安装步骤 https://zhuanlan.zhihu.com/p/519819303…

趋势外推法

趋势外推法主要利用图形识别法和差分法计算&#xff0c;进行模型的基本选择。 一、图形识别法。 这种方法是通过绘制散点图来进行的&#xff0c;即将时间序列的数据绘制成以时间 t 为横轴、时序观察值为纵轴的图形&#xff0c;观察并将其变化曲线与各类函数曲线模型的图形进行…