KVC

今天和大家讨论一下OC中KVC(KeyValueCoding)键值编码

KVC定义

KVC(KeyValueCoding)键值编码技术可以让我们在OC的开发中使用字符串作为Key来访问某个对象的属性值或者为某个对象的属性值进行赋值。而不需要通过暴露在头文件中的存取器方法进行访问和赋值操作。通过这种方法,我们可以获取某个对象并没有暴露在头文件中的实例变量和为这个实例变量进行赋值操作。通过这种方法可以绕过编译器的审查,使我们能够做出一些”不太合规“的操作,而不会让编译器报错。因此这种形式的存取是在运行时进行的,也是OC动态特性的一种体现。

KVC原理

NSObject有个分类(NSKeyValueCoding)关于KVC的实现都在里面,所以,所有继承自NSObject的对象都可以使用KVC,而结构体和某些Swift对象
因为没有继承NSObject而不可以使用

KVC取值设值以及查找顺序

KVC进行取值和设值操作时会根据你所传递的字符串按照一定规律进行查找,我们编写代码进行测试,以找到这个规律。

首先我们新建一个Person类

  • 取值

在我们程序入口利用KVC来获取Person示例的name属性

Person *zhangSan = [Person new];
    NSLog(@"%@",[zhangSan valueForKey:@"name"]);

在Person.m编写代码如下:


#import "Person.h"

@interface Person ()
{
    NSString *_name;//第一个查找的实例变量
    NSString *_isName;//第二个查找的实例变量
    NSString *name;//第三个查找的实例变量
    NSString *isName;//第四个查找的实例变量
    
}

@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        _name = @"ivar _name";
        _isName = @"ivar _isName";
        name = @"ivar name";
        isName = @"ivar isName";
        
    }
    return self;
}

//第一个取值查找的方法
-(NSString *)getName
{
    return @"getName method";
}
//第二个取值查找的方法
-(NSString *)name
{
    
    return @"name method";
}
//第三个取值查找的方法
-(NSString *)isName
{
    return @"isName method";
}
//第四个取值查找的方法
-(NSString *)_name
{
    return @"_name method";
}

//当成数组处理
-(NSInteger)countOfName
{
    return 2;
}

-(id)objectInNameAtIndex:(NSInteger)index
{
    return @"数组成员";
}
//是否可以直接操作实例变量,默认返回yes
+(BOOL)accessInstanceVariablesDirectly
{
    NSLog(@"accessInstanceVariablesDirectly");
    return YES;
}

@end

经过代码测试我们发现,当我们对Person的示例使用KVC来获取它的name属性时,系统先会按照顺序查找一系列方法是否存在如果存在便调用并拿到这个方法的返回值作为kvc取值的结果,这一系列的方法的顺序便是我上文代码中注释的顺序,分别是getName name isName _name,如果第一个方法就存在那便不再调用之后的方法,以此类推。

当四个方法全都没有找到时,我们的系统会把name当做数组去处理,看看是否实现了代码中的两个方法。如果实现了,就返回一个数组。

如果数组方法也没有找到,会当做集合去处理,在实现中查找相应的方法。

如果集合的相关方法也没有找到,系统会调用accessInstanceVariablesDirectly方法,这个方法是指示是否允许直接操作示例变量的,如果你没有重写这个方法,默认返回是yes。如果返回NO则程序不再往下执行,崩溃,如果返回yes,那么将会按照顺序查找相应的示例变量,他们的顺序是:_name _isName name isName;如果有对应的实例变量那就返回他的值,如果四个都查找不到,那么程序崩溃。

  • 赋值

赋值的方式和取值的方式类似也是先按顺序查找方法,如果方法都没有查找到,那么调用accessInstanceVariablesDirectly方法,如果返回yes则会按照同样的顺序查找相应的实例变量

在程序入口处:

    Person *zhangSan = [Person new];
   [zhangSan setValue:@"2341" forKey:@"name"];

在Person.m编写代码如下:

//第一个查找的方法
-(void)setName:(NSString *)name
{
    NSLog(@"setName %@",name);
}
//第二个查找的方法
-(void)_setName:(NSString *)name
{
    NSLog(@"_setName %@",name);
}

keypath

在KVC中还可以使用keypath路径来访问属性
举例说明:就刚刚的Person类我们给它创建一个lover属性,lover的类型也是Person那么我们想要获取某个Person对象的lover的name我们要怎么做呢?我们可以这样

    Person *zhangSan = [Person new];
   [zhangSan valueForKeyPath:@"lover.name"];

KVC允许我们使用.来连接属性这样我们可以方便的获取对象的属性的属性。。。

避免崩溃

正常情况下如果我们把nil设置给一个非对象属性,或者查找一个并不存在的key程序就会崩溃,我们通过重写下面的这些方法可以避免崩溃

-(void)setNilValueForKey:(NSString *)key
{
    NSLog(@"不允许设置为nil");
}

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    NSLog(@"不存在这个key");
}

-(id)valueForUndefinedKey:(NSString *)key
{
    NSLog(@"不存在这个key");
    return nil;
}

数字和结构体

当属性类型为数字或者结构体时,我们不能直接用数字和结构体来赋值,而是需要转化为NSNumber和NSValue来赋值,同样的,我们获取到的值也会被转化为NSNumber和NSValue类型。

数组的KVC应用

在数组中KVC有一些特有的运算符,我们一起看一下用法吧
我们编写代码如下:

     Person *zhangsan = [Person new];
    [zhangsan setValue:@10 forKey:@"age"];
    
    Person *lisi = [Person new];
    [lisi setValue:@20 forKey:@"age"];
    
    Person *wangwu = [Person new];
    [wangwu setValue:@30 forKey:@"age"];
    
    Person *zhaoliu = [Person new];
    [zhaoliu setValue:@10 forKey:@"age"];
    
    NSArray *peoples = @[zhangsan,lisi,wangwu,zhaoliu];

    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@min.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@max.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@sum.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@avg.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@count"]).integerValue) ;
    NSLog(@"%@",[peoples valueForKeyPath:@"@distinctUnionOfObjects.age"]);
    NSLog(@"%@",[peoples valueForKeyPath:@"@unionOfObjects.age"]);  

输出结果为

2018-09-29 16:57:49.628811+0800 KvcKvoTest[254:3290335] 10
2018-09-29 16:57:49.628903+0800 KvcKvoTest[254:3290335] 30
2018-09-29 16:57:49.629141+0800 KvcKvoTest[254:3290335] 70
2018-09-29 16:57:49.629283+0800 KvcKvoTest[254:3290335] 17
2018-09-29 16:57:49.629370+0800 KvcKvoTest[254:3290335] 4
2018-09-29 16:57:49.629520+0800 KvcKvoTest[254:3290335] (
    10,
    20,
    30
)
2018-09-29 16:57:49.629647+0800 KvcKvoTest[254:3290335] (
    10,
    20,
    30,
    10
)

前面的五种运算符相信大家都可以明白它的用法,后面的两个运算符distinctUnionOfObjects为将数组中的对应属性排序去重返回一个数组,unionOfObjects为将数组中的对应属性返回一个数组。

字典的KVC应用

字典也可以像普通对象一样使用kvc,因此valueForKeyPath可以很方便的用来操作多层字典。

还有两个方法用来字典转对象和对象转字典:

  • 对象转字典:
     Person *zhangsan = [Person new];
    [zhangsan setValue:@10 forKey:@"age"];
    [zhangsan setValue:@"zhangsan" forKey:@"name"];
    
    NSDictionary *dic= [zhangsan dictionaryWithValuesForKeys:@[@"age",@"name"]];
    NSLog(@"%@",dic);
  • 字典转对象:
Person *zhangsan = [Person new];
    [zhangsan setValuesForKeysWithDictionary:@{@"name":@"zhangsan",@"age":@10}];

KVC用途总结

使用KVC可以动态的取值,赋值,可以操作私有变量,可以修改一些系统控件,可以对数组进行一些操作,以及对象和字典的转换。
和所有OC的动态特性一样,如果滥用会容易导致崩溃和一些不好排查的问题出现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 元旦的时候,我给Z君发消息说,17岁的我认识你的第18年,元旦快乐。Z君很快回复了我,别提了,都是孽缘啊。...
    遗忘的罐罐阅读 202评论 0 2
  • 下午必须去实验小学讲课,开学第一讲《拒绝诱惑 快乐成长》;就是这么巧,也是这个时间段组长发布消息,必须听课、评课,...
    知心玲姐阅读 92评论 0 1
  • 农历丁酉年九月廿八,星期四,立冬第十天。 早上4:30闹铃起床,含贞爸蹲着听早课读经典:《易经》上经泰卦~大有卦;...
    育心经典包志刚阅读 343评论 0 4