当前位置: 首页 > news >正文

【iOS】KVC

【iOS】KVC

  • 前言
  • KVC常用方法
    • KVC设值
    • KVC取值
    • KVC使用keyPath
    • 字典与模型转化
      • 字典转模型
      • 模型转字典
    • KVC的nil异常处理
  • KVC的应用
    • KVC实现高阶的消息传递
  • 总结

前言

KVC,键值编码,是OC的一种间接访问对象属性方法或成员变量的机制。它允许通过字符串key名称来访问对应的属性方法或成员变量,而不是直接调用getter/setter方法

KVO传值的内部就是依赖于KVC实现属性值变化的检测和通知机制的

KVC常用方法

KVC设值

-(void)setValue:(id)value forKey:(NSString *)key;

这里了解一下KVC设值时的内部查找过程:

  • 查找setter方法

程序会优先调用setter方法-(void)setStr:(NSString *)str;,如果存在,就直接调用。

  • 如果没有setter方法,调用+(BOOL)accessInstanceVariablesDirectly;方法

KVC机制会检查该方法,如果返回YES(默认),表示允许直接访问成员变量;反之如果手动充血该方法,返回NO,则表示禁止访问,KVC不会再去找_age等变量,而是直接调用-(void)setValue:(id)value forUndefinedKey:(NSString *)key;,直接出触发异常,表示无法找到指定的key。

  • 若返回YES,允许访问实例变量,则按照以下顺序查找成员变量:
    • _ key
    • _isKey
    • key
    • isKey

找到第一个匹配的成员变量后,即对该变量直接赋值。对于我的例子,搜索顺序是:_str、 _isStr、 str、 isStr。

无论实例变量是在类接口还是类实现定义,或者使用何种访问修饰符(private、protected、public),KVC都能访问并赋值。

下面使用一个demo来测试体现一下KVC设置时内部的查找顺序:

#import <Foundation/Foundation.h>
#import "KVCTest.h"int main(int argc, const char * argv[]) {@autoreleasepool {KVCTest *test = [[KVCTest alloc] init];[test setValue:@"Tom" forKey:@"name"];NSLog(@"_name:%@", test->_name);NSLog(@"_isName:%@", test->_isName);NSLog(@"name:%@", test->name);NSLog(@"isName:%@", test->isName);NSLog(@"@property:%@", test.name);}return 0;
}
  1. 如果存在setter方法
@interface KVCTest : NSObject {@publicNSString *_name;NSString *_isName;NSString *name;NSString *isName;
}@property(nonatomic, strong) NSString *name;@end

输出:这里我们会发现不止 test.name 被设值了,_name 也被设值了,这是因为我们碰巧定义了一个和属性自动生成的成员变量同名的变量。

在这里插入图片描述

  1. 没有setter方法,accessInstanceVariablesDirectly方法默认返回YES
@interface KVCTest : NSObject {@publicNSString *_name;NSString *_isName;NSString *name;NSString *isName;
}

输出:按照 _str、 _isStr、 str、 isStr 顺序输出。

在这里插入图片描述

  1. accessInstanceVariablesDirectly方法手动重写返回NO
+(BOOL)accessInstanceVariablesDirectly {return NO;
}-(void)setValue:(id)value forKey:(NSString *)key {NSLog(@"出现异常,无法设置");
}

输出:返回NO后不再查找成员变量,直接调用 setValue:(id)value forKey:(NSString *)key 方法。

在这里插入图片描述

这样KVC的设值就很清楚了。

KVC取值

-(id)valueForKey:(NSString *)key;

和KVC设值一样,KVC取值也有既定的顺序:

  • 首先按照一下顺序查找gettert方法,找到直接调用取值:
    • getKey
    • key
    • isKey
    • _key

如果是BOOL或者int类型的值时,会将其包装成一个NSNumber对象。

  • 若以上所有方法都没有找到,则查看+ (BOOL)accessInstanceVariablesDirectly;方法的返回值,和设值一样,如果返回YES,则直接调用-(id)valueForUndefinedKey:(NSString *)key方法且抛出 NSUnknowKeyExpection 异常。
  • 若返回YES,和设值一样按照 _str、 _isStr、 str、 isStr 的顺序查找成员变量。

同样用一个demo展示一下KVC取值顺序:

KVCTest *test = [[KVCTest alloc] init];
[test setValue:@"4" forKey:@"age"];
id result = [test valueForKey:@"age"];
NSLog(@"结果:%@", result);
  1. 按顺序查找getter方法
-(int)getAge {return 1;
}-(int)age {return 2;
}-(int)isAge {return 3;
}

输出结果:

在这里插入图片描述

注释掉 getAge :

在这里插入图片描述

注释掉 age :

在这里插入图片描述

注释掉 isAge :

在这里插入图片描述

  1. accessInstanceVariablesDirectly; 方法返回NO
+(BOOL)accessInstanceVariablesDirectly {return NO;
}-(id)valueForUndefinedKey:(NSString *)key {NSLog(@"出现异常");return nil;
}

输出结果:

在这里插入图片描述

KVC使用keyPath

当我们要改变的对象是自定义类或者是其它复杂的数据类型时,我们使用keyPath这个方法来代替使用key一层一层监控的复杂操作。

key 和 keyPath 的区别在于:

  • key:只能接受当前类所具有的属性和从父类继承过来的属性。
  • keyPath:除了能接受当前类的属性,还能接受当前属性的属性,也就是可以接受关系链,然后进行深层访问。
@interface KVCNext : NSObject@property(nonatomic, strong) KVCTest *test;@end
KVCTest *test = [[KVCTest alloc] init];
KVCNext *test1 = [[KVCNext alloc] init];
[test1 setValue:test forKey:@"test"];
[test1 setValue:@"Timi" forKeyPath:@"test.name"];
NSLog(@"%@", [test1 valueForKeyPath:@"test.name"]);

这样就可以设置它的值了:

在这里插入图片描述

字典与模型转化

字典转模型

可以使用KVC的的 setValuesForKeysWithDictionary: 方法来传入一个字典,将字典的key和属性名进行匹配,把value赋值给对象的属性,以实现批量存值。

NSDictionary *dic1 = @{@"name":@"Boss", @"age":@"18", @"sex":@"男"};
Person *p1 = [[Person alloc] init];
[p1 setValuesForKeysWithDictionary:dic1];
NSLog(@"name:%@", p1.name);
NSLog(@"age:%ld", (long)p1.age);
NSLog(@"sex:%@", p1.sex);

在这里插入图片描述

但是如果遇到属性与字典key不匹配的情况,可以重·-(void)setValue:(id)value forUndefinedKey:(NSString *)key方法:

NSDictionary *dic2 = @{@"name":@"Timi", @"age":@"20", @"studentSex":@"女"};
Person *p2 = [[Person alloc] init];
[p2 setValuesForKeysWithDictionary:dic2];
NSLog(@"name:%@", p2.name);
NSLog(@"name:%ld", (long)p2.age);
NSLog(@"name:%@", p2.sex);

这种情况,我们的Person类中没有studentSex这个属性,因此如果不重写该方法,系统将会报错。

在这里插入图片描述

-(void)setValue:(id)value forUndefinedKey:(NSString *)key {NSLog(@"未定义的key:%@, value:%@", key, value);
}

这样当key不匹配时,调用这个函数,就会得到输出:

在这里插入图片描述

模型转字典

NSDictionary *tempDic = [dic1 dictionaryWithValuesForKeys:@[@"name", @"age", @"sex"]];
NSLog(@"tempDic:%@", tempDic);

在这里插入图片描述

KVC的nil异常处理

一般情况下,不允许KVC给一个对象赋值为nil,此时需要我们自己手动去处理nil异常的部分,通常我们需要重写- (void)setNilValueForKey:(NSString *)key方法。

KVCTest *test = [[KVCTest alloc] init];
[test setValue:nil forKey:@"age"];
NSLog(@"%@", [test valueForKey:@"age"]);

这里我们尝试将nil赋值给age,此时就会触发方法:

- (void)setNilValueForKey:(NSString *)key {if ([key isEqualToString:@"age"]) {NSLog(@"不能设置成nil状态%@", key);_age = 11;} else {[super setNilValueForKey:key];}
}

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

KVC的应用

KVC实现高阶的消息传递

对于容器使用一个KVC,实际上并没有对我们的容器进行一个操作,而是将这个方法传递给容器中的每一个元素,然后再重新返回一个容器。

换句话说,KVC可以让我们对数组中每个元素批量取值或调用属性,而不需要写循环。

NSArray *str = @[@"ios", @"web", @"server"];
//capitalizedString:NSString自带属性,返回一个首字母大写的新字符串
NSArray *strTemp = [str valueForKey:@"capitalizedString"];
for (NSString* arr in strTemp) {NSLog(@"%@", arr);
}
NSArray *strLength = [str valueForKeyPath:@"capitalizedString.length"];
for (NSNumber *length in strLength) {NSLog(@"%ld", (long)length.integerValue);
}

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

总结

以上就是关于对KVC的学习总结,之后还会继续学习相关内容。

http://www.dtcms.com/a/487310.html

相关文章:

  • 自己注册公司网站教育网站建设网站
  • 九度企业网站推广软件合肥网站建设工作
  • Beyond Vision (BV) -具有人工智能功能的全自动多旋翼无人机和混合垂直起降无人机
  • 做网站网络公司无收入阿里巴巴建站多少钱
  • 旅游网站对比模板下载08wordpress主题
  • HTTP之POST请求和GET请求
  • 聚类算法详解:k-means、层次聚类和DBSCAN
  • 廊坊中小企业网站制作做汽配找哪个网站好
  • 网站建设视频教程集工厂管理软件
  • Linux 学习笔记之 VI 编辑器与文件查找技巧
  • 贵阳网站建设多钱钱清远专业网站制作公司
  • 网站设计对网站搜索引擎友好性的影响南宁市两学一做网站
  • 什么是部署php网站邢台城乡建设局网站
  • 搜狐三季度营收多少新开网站seo
  • 基于 clangd 搭建 Redis 6.2 源码阅读与调试环境
  • 备案主体负责人 网站负责人哪个网站做ppt好
  • C文件中在编译时候怎么把几个同名函数链接进来
  • 影视 IP 全链开发:App 如何成为核心
  • 机房网络组建方案搜索引擎优化的核心是
  • CentOS7虚拟机扩容操作指南(CentOS7虚拟机磁盘大小不够)
  • Replication(下):事务,一致性与共识
  • PIL与OpenCV图像读取的颜色格式陷阱:RGB vs BGR
  • 佳能LBP6018L黑白激光打印机打印浅淡的一点尝试性解决方法
  • 网站主页面设计哪个好jizhicms
  • x86虚拟化漏洞与硬件辅助虚拟化演进要点
  • 做奥网站营销网站建设设计
  • Cocos Creator学习之性能优化
  • 分类信息多城市网站优秀网站设计要素
  • 从 “越充越坏” 到 “精准修复”:DCA-8000 动态诊断充电系统实操案例与问题解决
  • 做oa系统的网站网站开发方向的工作