关于KVC

KVC的定义

   键值编码是由NSKeyValueCoding非正式协议启用的一种机制,对象采用这种机制来提供对其属性的间接访问。当对象是键值编码兼容的对象时,可以通过简洁,统一的消息传递接口通过字符串参数来访问其属性。这种间接访问机制补充了实例变量及其关联的访问器方法提供的直接访问。

访问对象事属性

KVC 设值
  • setValue:forKey:将相对于接收消息的对象的指定键的值设置为给定值。setValue:forKey:自动实现代表标量和结构的自动包装NSNumber和NSValue对象的默认实现,并将其分配给属性。如果给非对象类型设置为nil,会走setNilValueForKey:并抛出异常。

如果指定的键对应于接收setter调用的对象所不具有的属性,则该对象将向自身发送setValue:forUndefinedKey:消息。默认实现setValue:forUndefinedKey:会引发一个NSUndefinedKeyException。但是子类可以重写setValue:forUndefinedKey:以自定义方式处理请求。

setValue forKey:的搜索模式:
1、先查找访问器方法,按顺序依次查set<Key>:_set<Key>:setIs<Key>:,如果存在某个方法,使用value调用该方法来设置属性值,查找完成。否则,执行第2步。

2、没有找到上述方法,如果类方法accessInstanceVariablesDirectly返回YES,依次查找实例变量_<key>_is<Key><key>is<Key>。如果找到,则直接使用value设置实例变量的值并完成操作。

3、如果访问器和实例变量都未找到,调用setValue:forUndefinedKey:。默认情况下会引发异常,但是的子类NSObject可能提供特定于键的行为。

image.png

                                  看图可能更好理解。

  • setValue:forKeyPath:在相对于接收器的指定键路径处设置给定值。密钥路径序列中不符合特定键的键值编码的任何对象都会收到一条setValue:forUndefinedKey:消息。
  • setValuesForKeysWithDictionary:使用字典键识别属性,以指定字典中的值设置接收器的属性。默认实现setValue:forKey:为每个键值对调用,nil并NSNull根据需要替换对象。

KVC 取值

  • valueForKey:返回由key参数命名的属性的值。如果根据描述的规则找不到由关键字命名的属性,则该对象向自身发送一条valueForUndefinedKey:消息。默认实现valueForUndefinedKey:会引发一个NSUndefinedKeyException,但是子类可以重写valueForUndefinedKey,处理一些异常。

valueForKey:的搜索模式:
1、依次搜索访问器方法get<Key><key>is<Key>_<key>。如果找到其中一个,调用它并继续执行步骤5。否则,继续下一步。

2、如果未找到访问器方法,搜索countOf<Key>objectIn<Key>AtIndex:,生成并返回NSKeyValueArray数组。(与NSArray该类定义的原始方法<key>AtIndexes:相对应)和(与该NSArray方法相对应)相匹配的方法objectsAtIndexes:

如果找到其中的第一个,再找到其他两个中的至少一个,则创建一个响应所有NSArray方法的集合代理对象并将其返回。否则,请继续执行步骤代理对象随后将任何NSArray接收到的一些组合的消息countOf<Key>objectIn<Key>AtIndex:<key>AtIndexes:消息给键-值编码创建它兼容的对象。如果原始对象还实现了名称类似的可选方法get<Key>:range:,则代理对象也会在适当时使用该方法。

3、查找名为countOf<Key>enumeratorOf<Key>memberOf<Key>:(对应于NSSet类定义的原始方法)的三个方法。如果三个方法全部存在,则创建一个能够响应NSSet类所有方法的集合代理对象,并返回该集合代理对象,查找完成。否则执行第4步。

以后在操作该集合代理对象时,集合代理对象会将其接收的任何NSSet消息转换为countOf<Key>enumeratorOf<Key>memberOf<Key>:消息的某种组合并发送给valueForKey:消息的接收对象。实际上,集合代理对象与兼容键值编码的对象一起工作,使得兼容键值编码的对象的集合属性的行为就像该属性是NSSet一样,即使它并不是。

4、如果访问器方法和集合访问方法组都未找到,并且accessInstanceVariablesDirectly返回YES,则按顺序依次查找实例变量_<key>_is<Key><key>或者is<Key>。如果找到一个,则直接获取实例变量的值并执行第5步。否则,执行第6步。

5、如果检索到的属性值是对象指针,直接返回结果,查找完成。
如果该值是支持的NSNumber,将其存储在NSNumber实例中并返回该实例。
如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。

6、如果上诉都未找到,会调用valueForUndefinedKey:抛出异常,但是子类可以重写该方法处理一些异常。
搜索的流程图如下:

image.png

  • valueForKeyPath:返回相对于接收者的指定密钥路径的值。密钥路径序列中不符合特定键的键值编码的任何对象(即,默认实现valueForKey:无法找到访问器方法)均会接收到valueForUndefinedKey:消息。
  • dictionaryWithValuesForKeys:返回相对于接收者的键数组的值。该方法调用valueForKey:数组中的每个键。返回的NSDictionary值包含数组中所有键的值。

特殊说明:集合对象,如NSArray,NSSet和NSDictionary,不能包含nil的值。而是nil使用NSNull对象表示值。NSNull提供一个表示nil对象属性值的实例,默认实现dictionaryWithValuesForKeys:和相关的自动setValuesForKeysWithDictionary:转换NSNull(在dictionary参数中)和nil(在storage属性中)。

访问集合属性

  • mutableArrayValueForKey:
    输入一个key,返回一个可变数组代理,该代理提供对给定键指定的有序对多关系的读写访问。

内部实现:mutableArrayValueForKey:内部动态创建了NSMutableArray的子类NSKeyValueMutableArray,然后指针混写指向子类,并返回子类。常与KVO搭配使用,比如:观察某个对象的arr属性,实际观察的是arr的子类NSKeyValueMutableArray。

  • mutableArrayValueForKeyPath:
    输入一个key路径,返回一个可变数组,该数组提供对给定键路径指定的有序对多关系的读写访问

  • mutableSetValueForKey:
    输入一个key,返回一个可变集合代理,该代理提供对给定键指定的无序对多关系的读写访问。

  • mutableSetValueForKeyPath:
    输入一个key路径,返回一个可变集合,该集合提供对给定键路径指定的无序对多关系的读写访问。

  • mutableOrderedSetValueForKey:
    输入一个key,返回一个可变的有序集合,该集合提供对给定键指定的唯一有序多对关系的读写访问。

  • mutableOrderedSetValueForKeyPath:
    输入一个key路径,返回一个可变的有序集合,该集合提供对给定键路径指定的唯一有序多对关系的读写访问。

使用集合运算符

①集合运算符

KVC提供了5中集合运算符,分别是:@avg, @count , @max , @min ,@sum。

示例:

  KVCPerson *person1 = [KVCPerson new];
  person1.dog.age = 1;

  KVCPerson *person2 = [KVCPerson new];
  person2.dog.age = 1;

  KVCPerson *person3 = [KVCPerson new];
  person3.dog.age = 3;

  KVCPerson *person4 = [KVCPerson new];
  person4.dog.age = 4;

  NSArray *arr  = @[person1, person2, person3, person4];
  NSNumber *sum = [arr valueForKeyPath:@"@sum.dog.age"];
  NSLog(@"sum:%.f", sum.floatValue);
  NSNumber *avg = [arr valueForKeyPath:@"@avg.dog.age"];
  NSLog(@"avg:%.f", avg.floatValue);
  NSNumber *count = [arr valueForKeyPath:@"@count"];
  NSLog(@"count:%.f", count.floatValue);
  NSNumber *min = [arr valueForKeyPath:@"@min.dog.age"];
  NSLog(@"min:%.f", min.floatValue);
  NSNumber *max = [arr valueForKeyPath:@"@max.dog.age"];
  NSLog(@"max:%.f", max.floatValue);

  打印结果:
  2021-03-26 00:23:48.619763+0800 Project[4156:128149] sum:9
  2021-03-26 00:23:48.620122+0800 Project[4156:128149] avg:2
  2021-03-26 00:23:48.620306+0800 Project[4156:128149] count:4
  2021-03-26 00:23:48.620562+0800 Project[4156:128149] min:1
  2021-03-26 00:23:48.620726+0800 Project[4156:128149] max:4
②数组运算符

@distinctUnionOfObjects@unionOfObjects,获取对象属性的值,并以数组的方式返回。
区别:@distinctUnionOfObjects会去重,而@unionOfObjects不会去重。

KVCPerson *person1 = [KVCPerson new];
person1.dog.age = 1;

KVCPerson *person2 = [KVCPerson new];
person2.dog.age = 1;

KVCPerson *person3 = [KVCPerson new];
person3.dog.age = 3;

KVCPerson *person4 = [KVCPerson new];
person4.dog.age = 4;

打印结果:
2021-03-26 00:29:23.011315+0800 Project[4208:131986] distinctUnionOfObjects
2021-03-26 00:29:23.011539+0800 Project[4208:131986] 3
2021-03-26 00:29:23.011702+0800 Project[4208:131986] 1
2021-03-26 00:29:23.011846+0800 Project[4208:131986] 4
2021-03-26 00:29:23.011975+0800 Project[4208:131986] unionOfObjects
2021-03-26 00:29:23.012168+0800 Project[4208:131986] 1
2021-03-26 00:29:23.012308+0800 Project[4208:131986] 1
2021-03-26 00:29:23.012445+0800 Project[4208:131986] 3
2021-03-26 00:29:23.012579+0800 Project[4208:131986] 4
③嵌套运算符

@distinctUnionOfArrays
当指定@distinctUnionOfArrays运算符时,valueForKeyPath:创建并返回一个数组,该数组包含与右键路径指定的属性相对应的所有集合的组合的不同对象。

@unionOfArrays
当指定@unionOfArrays运算符时,将valueForKeyPath:创建并返回一个数组,该数组包含与右键路径指定的属性相对应的所有集合的组合的所有对象,而不会删除重复项。

@distinctUnionOfSets
当指定@distinctUnionOfSets运算符时,将valueForKeyPath:创建并返回一个NSSet对象,该对象包含与右键路径指定的属性相对应的所有集合的组合中的不同对象。

该运算符的行为类似于@distinctUnionOfArrays,只是它期望NSSet包含NSSet对象实例,而不是NSArray实例的NSArray实例。同样,它返回一个NSSet实例。假设示例数据已存储在集合中而不是数组中,则示例调用和结果与所示相同@distinctUnionOfArrays。

运算符这块相对来说,集合运算符和数组运算符用的多一点,嵌套运算符用的相对较少。

总结

日常开发中,灵活的运用KVC可以更加方便的实现某些功能,并且使代码更加简洁。

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

推荐阅读更多精彩内容