Runtime-KVC
2020/7/14 23:09:51
本文主要是介绍Runtime-KVC,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
KVC
KVC又叫做键值编码,不借助类提供的接口(setter和getter),访问类中的属性和成员变量,即使是私有属性和成员变量,也可以通过KVC进行读写,大大提高编程灵活性,但也破坏了系统封装性。
KVC是借助runtime,来获取类中信息,从而进行数据的读写。NSObject类的一个分类中定义了KVC的功能,因此只有继承NSObject的类才可以使用KVC。
KVC使用方式
KVC可以访问类中的基本数据类型、结构体、非集合对象和集合对象。类的结构并不是只有一层,对于属性或者成员变量也是一个类的多层类,KVC可以实现读写。
forKey
使用forKey只能访问类中单层属性。
- (void)setValue:(nullable id)value forKey:(NSString *)key; - (nullable id)valueForKey:(NSString *)key; 复制代码
forKeyPath
使用forKey可以访问类中深层属性,即只有类的属性或者成员变量是一个类,就可以通过KVC访问这个类中的属性。
- (nullable id)valueForKeyPath:(NSString *)keyPath; - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; 复制代码
基本类型和结构体的KVC
由于KVC是基于NSObject的,因此使用KVC读写基本类型或者结构体时,需要将其转换成NSNumber或者NSValue。
ZPPerson *p = [ZPPerosn new]; // 对于基本类型读写,需要转换成NSNumber。 [p servalue:@(12) forKey:@"age"]; NSNumber *num = [p valueForKey:@"age"]; // 对于结构体类型读写,需要转换成NSValue。 typedef struct { int chinese; int math; int english; } GradeStruct; // 成绩 GradeStruct gds = {92, 100, 34}; NSValue *valueOfGradeStruct = [NSValue value:&gds withObjCType:@encode(GradeStruct)]; [p setValue:valueOfGradeStruct forKey:@"gds"]; GradeStruct gds2; [[p valueForKey:@"gds"] getValue:&gds2]; 复制代码
集合操作
数组
对于数组的KVC操作有两种:
1. 数组的聚集函数,sum、avg、min、max计算元素属性的总和、平均值、最小、最大 2. 属性为数组,元素为自定义对象,读取数组元素中的某个属性。 复制代码
@interface ZPPerson5 : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, strong) NSMutableArray<ZPPerson6 *> *friends; @end NSLog(@"%@",[p valueForKeyPath:@"friends.@sum.age"]); NSLog(@"%@",[p valueForKeyPath:@"friends.@avg.age"]); NSLog(@"%@",[p valueForKeyPath:@"friends.@min.age"]); NSLog(@"%@",[p valueForKeyPath:@"friends.@max.age"]); NSArray *arr = [p mutableArrayValueForKeyPath:@"friends.age"]; 复制代码
字典
KVC提供了批量的读写数据的方式,字典与模型的相互转换,支持属性和成员变量,支持基本类型、结构体、OC类型。
// ZPPerson5.h @interface ZPPerson5 : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, strong) ZPPerson5 *father; @property (nonatomic, strong) NSMutableArray *friends; @end // ZPPerson5.m @interface ZPPerson5 () { BOOL _sex; NSString * test1; NSString * _test2; NSString * isTest3; NSString * _isTest4; NSMutableArray *_arrms; NSSet *_sets; GradeStruct _gds ; } @end // KVCy提供了两个关于字典的方法,实现字典与模型的转换。 ZPPerson6 * p = [ZPPerson new]; // 模型转字典 NSDictionary *dictOfPerson = [p dictionaryWithValuesForKeys:@[@"age", @"name", @"father", @"test1", @"_test2", @"isTest3", @"_isTest4"]]; // 字典转模型 NSDictionary *dict = @{@"name":@"王司徒", @"age":@(76), @"test1":@"ggggg", @"_test2":@"jjjjj", @"isTest3":@"kkkkk", @"_isTest4":@"vvvvv"}; [p setValuesForKeysWithDictionary:dict]; 复制代码
KVC搜索key的步骤
setValue:forKey:和setValue:forKeyPath:
1、查询是否有set:方法,有则调用,KVC结束。 2、系统调用该类中实现的accessInstanceVariablesDirectly方法,是否还要继续搜索,YES则继续向下走,NO则直接跳到第4步。 3、按照_key、_isKey、key、isKey顺序查找是否相关的成员变量,找到则KVC结束,没有找到进入第4步。 4、上面步骤走完没有找到,调用setValue:forUndefinedKey:方法,如果不实现程序崩溃。
valueForKey:和valueForKeyPath:
1、查询是否有get,,is方法,有则调用。 2、查询是否有countOf、objectInAtIndex、AtIndexes方法,如果countOf方法和另外两个方法中的一个被找到,那么就会返回一个可以响应NSArray所有方法的代理集合(它是NSKeyValueArray。 3、查找countOf,enumeratorOf,memberOf格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet所的方法的代理集合。 4、系统调用+accessInstanceVariablesDirectly方法,是否还要继续搜索,返回NO,则直接进入第6步。 5、按照_,_is,,is的顺序搜索成员变量名。 6、调用valueForUndefinedKey:方法,如果没有实现则系统崩溃。
总结:
1、从上面的步骤可以看出,KVC首先搜索setter和getter,因此即使没有定义成员变量只是实现了setter和getter方法,可以用使用KVC。
2、KVC只会搜索相应名称的setter、getter或者成员变量,因此像分类中定义属性时,property_list有key对应属性,而ivar_list没有对应成员变量,又没有实现相应setter和getter方法,使用KVC会导致找不到key而程序崩溃。
KeyPath的搜索步骤
// ZPPerson.h @interface ZPPerson5 : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, strong) ZPPerson5 *father; @property (nonatomic, strong) NSMutableArray *friends; @end // ZPPerson5.m @interface ZPPerson5 () { ZPIDCard * _card; } @end @implementation ZPPerson5 - (instancetype)init { self = [super init]; if (self) { _card = [ZPIDCard new]; return self; } return nil; } // 当使用KVC查找到一定程度时,系统会调用该方法,询问是否还需要向下查找。 + (BOOL)accessInstanceVariablesDirectly { NSLog(@"ZPPerson5,查找到一定程度,是否还要继续搜索"); return NO; } @end // ZPCard @interface ZPIDCard : NSObject @end @interface ZPIDCard() { NSString * _idCard; } @end @implementation ZPIDCard +(BOOL)accessInstanceVariablesDirectly{ NSLog(@"ZPIDCard,查找到一定程度,是否还要继续搜索"); return NO; } @end // main ZPPerson5 *p = [ZPPerson5 new]; [p setValue:@"asdfsfd" forKeyPath:@"idCard._idCard"]; 复制代码
分别控制ZPPerson5和ZPIDCard类中的+accessInstanceVariablesDirectly方法。
1、ZPPerson5返回NO,ZPIDCard无所谓,终端打印ZPPerson5类中acess..方法,系统崩溃。
2、ZPPerson5返回YES,ZPIDCard返回NO,终端打印ZPIDCard类中acess..方法,系统崩溃。
由此可以得出,KeyPath其实还是与key的搜索步骤一样,例如"idCard._idCard",查找ZPPerson5类中是否有“idCard”对应的setter或者成员变量,如果找到进入该成员变量的类中(ZPIDCard),查找ZPIDCard中"__idcard"对应的setter或者成员变量。
KVC异常处理
KVC中的两种异常:
1、给基本数据类型或者结构体设置nil。
2、找不到key。
设置nil
iOS允许使用KVC给OC对象设置nil,但不允许使用KVC给值类型设置nil。
nil是给OC对象用来初始化的,不能赋值给值类型,因此如果使用KVC操作的成员变量如果是值类型,设置nil时,系统崩溃。
当KVC给值类型设置nil时:
> 1、查看该类是否实现setNilValueForKey:方法,实现则调用并结束KVC,否则进行第2步。 > > 2、程序崩溃。 复制代码
找不到key
当KVC找不到对应的key时:
1、查看该类是否实现了setValue:forUndefinedKey:或者valueForUndefinedKey:,实现了则调用该方法,并结束KVC,否则执行第2步。
2、程序崩溃。
使用keyPath时,和使用key一样,字符串中哪个节点触发了着两种异常,则查看该节点对应的类是否实现了相关方法。例如上面的"idCard._idCard",如果查找不到"__idCard",则查看ZPIDCard类是否实现相关方法。
KVC的键值验证
该功能其实是,用来验证key对应的value是否符合要求,需要在使用KVC的类中实现相应的验证方法。
@interface ZPPerson5 : NSObject @property (nonatomic, assign) NSInteger age; @end @implementation ZPPerson5 - (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError * _Nullable __autoreleasing *)outError { NSNumber *age = *ioValue; if (age.integerValue < 10) { return NO; } return YES; } @end // main ZPPerson5 *p = [ZPPerson5 new]; NSNumber *age = @(5); NSError * __autoreleasing error = nil; // 指向指针的指针需要是否__autoreleasing修饰 BOOL s = [p validateValue:&age forKeyPath:@"age" error:&error]; NSLog(@""); 复制代码
这篇关于Runtime-KVC的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值