iOS——KVC(键值编码)

news2024/10/4 10:32:17

键值编码(KVC)

KVC(Key Value Coding)是一种允许以字符串形式间接操作对象属性的方式。
最基本的KVC是由NSKeyValueCoding协议提供支持,最基本的操作属性如下:

  • setValue: 属性值 forKey: 属性名:为指定属性设置值;
  • valueForKey: 属性名:获取指定属性的值
    代码演示:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface AUser : NSObject

@property (nonatomic, copy) NSString *str1;
@property (nonatomic, copy) NSString *str2;

@end

NS_ASSUME_NONNULL_END


#import <Foundation/Foundation.h>
#import "AUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AUser *aUser = [[AUser alloc] init];
        //使用KVC方式为str1属性设置值
        [aUser setValue:@"astr11" forKey:@"str1"];
        //使用KVC方式为str2属性设置值
        [aUser setValue:@"astr22" forKey:@"str2"];
        //使用KVC方式获取AUser对象的属性值
        NSLog(@"str1: %@", [aUser valueForKey:@"str1"]);
        NSLog(@"str2: %@", [aUser valueForKey:@"str2"]);
    }
    return 0;
}

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

在使用KVC时,都是通过字符串来指定被操作的属性。即使用forKey传入属性名的字符串。

对于setValue: forKey: 方法,底层的执行机制如下:

  1. 程序优先考虑调用属性的setter方法
  2. 如果该类没有setter方法,KVC机制会搜索该类中名为传入的“_该字符串”的成员变量(大部分时候即创建属性的时候自动创建的成员变量)无论该成员变量是在接口或者实现部分定义、无论它用哪个访问控制符修饰,这条KVC底层上是对该成员变量的赋值。
    如果该类即没有setter方法也没有“_name”成员变量,那么KVC机制会搜索该类中名为name的成员变量(大部分时候即我们自己定义的成员变量)(与上条一样)
    如果上面3步都没有找到,那么系统会执行该对象的setValue: forUndefinedKey:方法,该方法的实现就是引发一个异常,导致程序结束
    valueForKey方法其他与上面一样,但是它获取的是getter方法的返回值。没有找到成员变量会执行valueForUndefinedKey:方法,该方法也会引起异常导致程序关闭。
    代码举例:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface AUser : NSObject {
    @package
    NSString *name;
    NSString *_name;
}

@end

NS_ASSUME_NONNULL_END


#import "AUser.h"

@implementation AUser {
    int age;
}
@end


#import <Foundation/Foundation.h>
#import "AUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AUser *aUser = [[AUser alloc] init];
        //使用KVC给属性赋值,KVC的搜素顺序为:
        //1、setName方法;2、_name成员变量;3、name成员变量
        //因此,在此处我们是先搜索到了_name成员变量,所以是给_name赋了值,name没有赋值
        //因此name为空
        [aUser setValue:@"strName1" forKey:@"name"];
        NSLog(@"name = %@", aUser->name);
        NSLog(@"_name = %@", aUser->_name);
        //虽然age成员变量是在实现部分定义的,但是它还是会被赋值
        [aUser setValue: [NSNumber numberWithInt:5] forKey:@"age"];
        NSLog(@"age = %@", [aUser valueForKey:@"age"]);
    }
    return 0;
}

处理不存在的Key

前面说过,使用KVC时,如果该属性没有setter、getter方法,也不存在对应的成员变量时,程序会调用setValue: forUndefinedKey:或valueForUndefinedKey:方法。系统默认该方法的实现是引发一个异常然后结束程序,但是我们可以重写这个方法,使其达到我们想要的效果。
只需要在FKApple类实现部分重写setValue:forUndefinedKey:方法,甚至不需要在类接口
声明该方法,例:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface AUser : NSObject {
    @package
    NSString *name;
    NSString *_name;
}

@end

NS_ASSUME_NONNULL_END
#import "AUser.h"

- (void) setValue: (id)value forUndefinedKey:(nonnull NSString *)key {
    NSLog(@"重写了setValue:value forUndefinedKey方法");
}

@end
#import <Foundation/Foundation.h>
#import "AUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AUser *aUser = [[AUser alloc] init];
        [aUser setValue:@"strName1" forKey:@"1"];
        NSLog(@"name = %@", aUser->name);
        NSLog(@"_name = %@", aUser->_name);
    }
    return 0;
}

处理nil值

假如我们在一个类中定义两个属性,一个属性是NSString类型的,一个属性是int类型的。当我们给两个属性赋nil值时,NSString属性是可以被赋nil值的,而int类型的值被赋nil时会引发异常,是由于int类型的属性不能接受nil值所导致的。
也就是说,当程序尝试给某个属性设置nil值时,如果该属性并不能接受nil值,那么程序会自动执行该对象的setNilValueForKey:方法。我们同样可以重写该方法来达到我们想要的效果。例如,接下来我们重写该方法,定义一个int类型的属性age,重写该方法使得如果给age属性赋nil值时,就将age赋值为0。代码:

#import "AUser.h"

@implementation AUser {
    int age;
}

- (void) setNilValueForKey:(NSString *)key {
    //如果尝试将key为name的属性设置为nil
    if ([key isEqualToString:@"age"]) {
        //将_name设置为0
        age = 0;
    } else {
        //回调父类的setNilValueForKey:执行默认行为
        [super setNilValueForKey:key];
    }
}

@end
#import <Foundation/Foundation.h>
#import "AUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AUser *aUser = [[AUser alloc] init];
        //使用KVC给age属性传nil
        [aUser setValue: nil forKey:@"age"];
        NSLog(@"age = %@", [aUser valueForKey:@"age"]);
    }
    return 0;
}

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

key路径

KVC除了可以操作对象的额属性之外,还可以操作对象的“复合属性”。所谓“复合属性”,KVC机制将其称为key路径。例如:AUser类里面包含着一个BUser类型的bUser属性,bUser对象中又包含着b1属相和b2属性,那么KVC可以通过bUser.b1、bUser.b2这种key路径来支持操作AUser对象的bUser属性的b1和b2属性。
根据key路径设置属性值的方法:

  • setValue: forKeyPath:根据key路径设置属性值
  • valueForKeyPath: 根据key路径获取属性值
    代码示例:
    AUser:
#import <Foundation/Foundation.h>
#import "BUser.h"

NS_ASSUME_NONNULL_BEGIN

@interface AUser : NSObject {
    @package
    BUser *bUser;
}

@property (nonatomic, assign) int aNumber;

@end

NS_ASSUME_NONNULL_END

BUser:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BUser : NSObject

@property (nonatomic, copy) NSString *b1;
@property (nonatomic, copy) NSString *b2;

@end

NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "AUser.h"
#import "BUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AUser *aUser = [[AUser alloc] init];
        [aUser setValue:@"12" forKey:@"aNumber"];
        [aUser setValue:[[BUser alloc] init] forKey:@"bUser"];
        [aUser setValue:@"这是b1" forKeyPath:@"bUser.b1"];
        [aUser setValue:@"这是b2" forKeyPath:@"bUser.b2"];
        NSLog(@"aNumber: %@", [aUser valueForKey:@"aNumber"]);
        NSLog(@"b1: %@", [aUser valueForKeyPath:@"bUser.b1"]);
        NSLog(@"b2: %@", [aUser valueForKeyPath:@"bUser.b2"]);
    }
    return 0;
}

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

实际上,通过KVC操作对象的性能比通过getter、setter方法操作的性能更差,使用KVC的优点是编程更加灵活,更适合提炼一些通用性质的代码

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

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

相关文章

Flink1.14 Source概念入门讲解与源码解析

目录 Flink Source概念 Source Source源码 getBoundedness() createReader(SourceReaderContext readerContext) createEnumerator(SplitEnumeratorContext enumContext) SplitEnumerator restoreEnumerator(SplitEnumeratorContext enumContext, EnumChkT checkpoint) …

使用Selenium进行网页登录和会话管理

随着互联网的快速发展&#xff0c;网页登录和会话管理是许多网站和应用程序的基本功能。通过网页登录&#xff0c;用户可以访问个人账户、购物车订单、历史记录等个性化信息。为了提高用户体验和效率&#xff0c;自动化登录和会话管理成为一个重要的需求。而Selenium作为一种强…

信创办公–基于WPS的EXCEL最佳实践系列 (限制可录入内容)

信创办公–基于WPS的EXCEL最佳实践系列 &#xff08;限制可录入内容&#xff09; 目录 应用背景操作过程1、数据有效性&#xff08;支出证明单&#xff09;2、如何完成数据有效性的使用&#xff08;差旅报销申请表&#xff09;3、清除数据验证4、利用圈释无效数据&#xff0c;验…

Docker容器中的SSH免密登录

简介&#xff1a;在日常的开发和测试环境中经常需要创建和管理Docker容器。有时&#xff0c;出于调试或管理的目的&#xff0c;可能需要SSH到容器内部。本文将介绍如何创建一个Docker容器&#xff0c;它在启动时自动运行SSH服务&#xff0c;并支持免密登录。 历史攻略&#xf…

在github上设置不同分支,方便回滚

在github上设置不同分支&#xff0c;方便回滚 步骤可能出现的问题couldnt find remote ref gpuVersion1. 确保您处于正确的分支2. 添加并提交更改&#xff08;如果还未进行&#xff09;3. 推送本地分支到远程仓库4. 验证操作 步骤 之前在github上上传了一个项目代码&#xff0c…

用Win10自带画图3D抠图

文章目录 一、打开“画图3D”二、插入图片三、抠图操作四、保存抠图 一、打开“画图3D” 在搜索框输入“画图3D” 选择彩色水滴图标的软件 二、插入图片 选择“新建” 导航栏“菜单”–>“插入”&#xff0c;选择要扣的图片。&#xff08;我选择了一张自己随意画的图片…

【动态规划刷题 17】回文子串 最长回文子串

647. 回文子串 链接: 647. 回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由…

启动 React APP 后经历了哪些过程

本文作者为 360 奇舞团前端开发工程师 前言 本文中使用的React版本为18&#xff0c;在摘取代码的过程中删减了部分代码&#xff0c;具体以源代码为准。 在React 18里&#xff0c;通过ReactDOM.createRoot创建根节点。并且通过调用原型链上的render来渲染。 本文主要是从以下两个…

Python函数绘图与高等代数互融实例(五): 则线图综合案例

Python函数绘图与高等代数互融实例(一):正弦函数与余弦函数 Python函数绘图与高等代数互融实例(二):闪点函数 Python函数绘图与高等代数互融实例(三):设置X|Y轴|网格线 Python函数绘图与高等代数互融实例(四):设置X|Y轴参考线|参考区域 Python函数绘图与高等代数互融实例(五…

iOS应用程序数据保护:如何保护iOS应用程序中的图片、资源和敏感数据

目录 转载&#xff1a;怎么保护苹果手机移动应用程序ipa中文件安全&#xff1f; 前言 1. 对敏感文件进行文件名称混淆 2. 更改文件的MD5值 3. 增加不可见水印处理 3. 对html&#xff0c;js&#xff0c;css等资源进行压缩 5. 删除可执行文件中的调试信息…

关于分布式一致性

一致性&#xff08;consistency&#xff09; 说到一致性&#xff0c;我们可能最先想到的数据库里的事务 这里的讨论的是分布式的一致性&#xff0c;事务就简化一下&#xff0c;只考虑Read/Write 先列举一下事务的种类&#xff1a; 单机的事务&#xff1a;多个复杂事务发生在一…

React组件化开发

1.组件的定义方式 函数组件Functional Component类组件Class Component 2.类组件 export class Profile extends Component {render() {console.log(this.context);return (<div>Profile</div>)} } 组件的名称是大写字符开头&#xff08;无论类组件还是函数组件…

ElementUI之登录与注册

目录 一.前言 二.ElementUI的简介 三.登录注册前端界面的开发 三.vue axios前后端交互--- Get请求 四.vue axios前后端交互--- Post请求 五.跨域问题 一.前言 这一篇的知识点在前面两篇的博客中就已经详细详解啦&#xff0c;包括如何环境搭建和如何建一个spa项目等等知识…

【二叉树魔法:链式结构与递归的纠缠】

本章重点 二叉树的链式存储二叉树链式结构的实现二叉树的遍历二叉树的节点个数以及高度二叉树的创建和销毁二叉树的优先遍历和广度优先遍历二叉树基础oj练习 1.二叉树的链式存储 二叉树的链式存储结构是指&#xff0c;用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑…

GitHub Copilot Chat

9月21日&#xff0c;GitHub在官网宣布&#xff0c;所有个人开发者可以使用GitHub Copilot Chat。用户通过文本问答方式就能生成、检查、分析各种代码。 据悉&#xff0c;GitHub Copilot Chat是基于OpenAI的GPT-4模型打造而成&#xff0c;整体使用方法与ChatGPT类似。例如&…

帆软BI开发-Day2-趋势图的多种变形

前言&#xff1a; 在BI数据展示中&#xff0c;条形图、趋势图无疑是使用场景非常多的两种图形。与条形图不同的是&#xff0c;趋势图更能反馈出一定的客观规律和未来的趋势走向&#xff0c;因此用于作为预警和判异的业务场景&#xff0c;但实际业务场景的趋势图可没你想的那么简…

代码随想录算法训练营 动态规划part14

一、最长公共子序列 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int longestCommonSubsequence(String s1, String s2) {int n s1.length(), m s2.length();char[] cs1 s1.toCharArray(), cs2 s2.toCharArray();int[][] f n…

精华回顾:Web3 前沿创新者在 DESTINATION MOON 共话未来

9 月 17 日&#xff0c;由 TinTinLand 主办的「DESTINATION MOON: Web3 Dev Summit Shanghai 2023」线下活动在上海黄浦如约而至。 本次 DESTINATION MOON 活动作为 2023 上海区块链国际周的 Side Event&#xff0c;设立了 4 场主题演讲与 3 个圆桌讨论&#xff0c;聚集了诸多…

[python 刷题] 42 Trapping Rain Water

[python 刷题] 42 Trapping Rain Water 题目&#xff1a; Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. 这题的前置我觉得至少还是得做过 11 Container With Most Wat…

神经辐射场(Neural Radiance Field,NeRF)的简单介绍

参考文章&#xff1a;https://arxiv.org/abs/2210.00379 1. 概述 神经辐射场&#xff08;NeRF&#xff09;模型是一种新视图合成方法&#xff0c;它使用体积网格渲染&#xff0c;通过MLP进行隐式神经场景表达&#xff0c;以学习3D场景的几何和照明。   应用&#xff1a;照片…