KVC

原文链接

Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties. When an object is key-value coding compliant, its properties are addressable via string parameters through a concise, uniform messaging interface. This indirect access mechanism supplements the direct access afforded by instance variables and their associated accessor methods.

以上是Apple对KVC的定义,翻译过来就是:

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

所有直接或者间接继承NSObject的对象都遵守 NSKeyValueCoding 协议,并默认提供实现,找到 NSKeyValueCoding.h 我们可以看到里面为这些类添加了一些extension


@interface NSObject(NSKeyValueCoding)

@interface NSArray<ObjectType>(NSKeyValueCoding)

@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)

@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)

@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)

@interface NSSet<ObjectType>(NSKeyValueCoding)


下面是一些主要的方法:


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

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

- (nullable id)valueForKeyPath:(NSString *)keyPath;
  
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

- (nullable id)valueForUndefinedKey:(NSString *)key;

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


实践是检验真理的唯一标准,下面创建一个ComandLine工程实践一下

  • Xcode -> New -> MacOS -> CommandLine
  • 创建Person类

关于KVC要分为两部分,集合类型与非集合类型,下面先来看非集合类型

Person.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic ,copy) NSString *name;

@property (nonatomic ,assign) NSUInteger age;

@property (nonatomic ,copy) NSArray<Person *> *friends;

@end

NS_ASSUME_NONNULL_END

Person.m

#import "Person.h"

@implementation Person

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        _name = @"";
        _age = 0;
        _friends = @[];
    }
    
    return self;
}

// 重写以便打印对象的属性
- (NSString *)description {
    
    return [NSString stringWithFormat:@"- name: %@, age: %ld, friends: %@",self.name, self.age, self.friends];
}

@end

打开main.m,创建两个Person的实例Alice和Bob

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *Alice = Person.new;
        Alice.name = @"Alice";
        Alice.age = 18;
        Alice.friends = @[];
        
        Person *Bob = Person.new;
        [Bob setValue:@"Bob" forKey:@"name"];
        [Bob setValue:@(28) forKey:@"age"];
        [Bob setValue:@[Alice] forKey:@"friends"];
        
        NSLog(@"%@",@[Alice,Bob]);
        NSLog(@"%@",[Bob valueForKeyPath:@"friends"]);
    }
    return 0;
}

其中,ALice使用正常的set访问器进行赋值,Bob使用KVC方式赋值,
Command + R,能够看到如下输出

(
    "- name: Alice, age: 18, friends: (\n)",
    "- name: Bob, age: 28, friends: (\n    \"- name: Alice, age: 18, friends: (\\n)\"\n)"
)

(
    "- name: Alice, age: 18, friends: (\n)"
)

输出正如我们想的那样,通过KVC,正确的对Bob的name,age,friends进行了赋值和取值操作

那么若果想直接获取Bob名字的长度呢

id nameLength = [Bob valueForKeyPath:@"name.length"];

有没有发现这种方式很便捷呢,后面还有更有趣的用法。

接下来想一个问题,如果我想使用KVC进行赋值但是相应的类并没有对应的属性怎么办

[Bob setValue:@"Bob" forKey:@"familyName"];

运行就会崩溃掉,而在生产环境的情况下我们可不想让App Crash,
可以看下报错信息

 Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x1030063b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key familyName.'

可以发现导致崩溃的原因是Person类没有实现这个方法 setValue:forUndefinedKey:
接下来在Person.m里面添加这个方法的实现

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    
}

可以看到运行之后不会崩溃了

上面简单介绍了一下非集合类型,下面先来看集合类型

首先new一个Array

NSArray *family = @[Alice,Bob];
NSLog(@"%@",[family valueForKey:@"count"]);

运行后发现又又又crash了,

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x100703db0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key count.'

按照正常的理解,我们无非是想回去Array的count属性,为什么会崩溃呢,原来对集合类型进行KVC操作时是分别对集合内对象进行操作,

NSLog(@"%@",[family valueForKey:@"name"]);

现在的输出是一个Array

(
    Alice,
    Bob
)

使用KVC对集合类型内部对象赋值也是一样的

NSArray *family = @[Alice,Bob];
[family setValue:@"NoName" forKey:@"name"];
NSLog(@"%@",family);

当然,你也可以自己重写上述的方法来使用,这里就不多赘述。

下面来还有一些更有趣的

KVC集合操作符

image

可以看到对于集合操作,在keypath中间我们可以加入一个集合操作符,能够实现更为便捷的骚操作

集合操作符有一下几种

  • @avg // 算数平均值
  • @count // 计数
  • @max // 最大值
  • @min // 最小值
  • @sum // 求和
  • @unionOfObjects // 创建并返回一个数组,该数组包含与右键路径指定的属性对应的集合的所有对象。
  • @distinctUnionOfObjects // 同unionOfObjects,但会对数组去重
  • @unionOfArrays // 创建并返回一个数组,该数组包含与右键路径指定的属性对应的所有集合的组合的对象。
  • @distinctUnionOfArrays // 同unionOfArrays,但会对数组去重
NSLog(@"%@",[family valueForKeyPath:@"@max.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@min.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@sum.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@avg.age"]);

这里可以看到获取到了Alice和Bob年龄的最大值,最小值,和,平均值

还有一种特殊场景,就是我想获取这个family的所有friends怎么做呢

NSLog(@"%@",[family valueForKeyPath:@"@unionOfObjects.friends"]);

(
        (
    ),
        (
        "- name: Alice, age: 18, friends: (\n)"
    )
)

可以看到打印似乎有些不对劲,我们想要的是所有的friends,但数组中却是Alice的friends,Bob的friends两个对象。
这时候我们可以使用unionOfArrays,这个操作符会帮我们将数组元素【铺平】

NSLog(@"%@",[family valueForKeyPath:@"@unionOfArrays.friends"]);

一句代码即展开获取了你所需要的Array,否则你只能写一些胶水代码

NSArray *arr = [Alice.friends arrayByAddingObjectsFromArray:Bob.friends];

不够优雅

基本的KVC就介绍这么多,如果有更生层次的自定义实现需求的话还是推荐多看看Apple的文档,不过记得使用KVC的话当对象更新了property不要忘记顺便也要改下keypath

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

推荐阅读更多精彩内容

  • KVC KVC(Key-Value-Coding)是Cocoa框架为我们提供的非常强大的工具,简译为键值编码。iO...
    Longshihua阅读 377评论 0 6
  • 本文参考: KVC官方文档 KVC原理剖析 iOS KVC详解 KVC 简介 KVC全称是Key Value Co...
    拧发条鸟xds阅读 5,282评论 6 23
  • 原文:iOS 关于KVC的一些总结 本文参考: KVC官方文档 KVC原理剖析 iOS KVC详解 KVC 简介 ...
    liyoucheng2014阅读 929评论 0 3
  • 关于键值编码 键值编码(KVC)是一种由NSKeyValueCoding非正式协议提供的机制,对象采用该机制来提供...
    渐z阅读 904评论 0 0
  • 宇航为什么要回国创业?而且是放弃几年的百万年薪回国创业?我和大东开始很不理解,和亲属同事朋友谈起时,大家也都...
    天泽家人阅读 471评论 0 2