浅谈 KVC 查找规则

简介

KVCKeyValue Coding 的简称,遵循 NSKeyValueCoding 协议,它是一种可以直接通过字符串的名字 key 来访问类属性的机制,而不是通过调用 settergetter 方法访问。

对于 KVCCocoa 自动放入和取出基本数据类型放入 NSNumberNSValue 中,当使用 setValue:ForKey: 或者 valueForKey: 时,它自动将基本数据类型从这些对象中取出,仅 KVC 具有这种自动包装功能,常规方法调用和属性语法不具备该功能。

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

NSKeyValueCoding

+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供属性值正确性�验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。

- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。

- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。

例如,如果用 KVC 访问 BankAccount 类中并不存在的变量 age 时,直接Crash 了:

image.png

控制台打印出

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

valueForUndefinedKey:方法抛出的异常,BankAccount 类重写 valueForUndefinedKey: 系统方法:

image.png

从控制台打印的结果上可以看出,当 KVC 找不到 age 这个值时,会查找 valueForUndefinedKey: 方法。

KVC 的查找流程

setValue:forKey 的查找方式:

以字符串的形式向对象发送消息,按照 set<Key>_set<Key> 命名规则查找,如果找到,传递参数,然后调用该方法;如果没有找到,则会查看类方法 accessInstanceVariablesDirectly 的返回值。如果返回 YES,那么将在对象内部查找名为 _<key>_is<Key><key>is<key> 的实例变量。如果找到则设置成员的值,如果没有查找调用 setValue:forUndefinedKey:,并抛出异常。

注意:

  • accessInstanceVariablesDirectly 默认返回值为 YES
  • accessInstanceVariablesDirectly 的返回值为 NO,直接调用setValue:forUndefinedKey:,抛出异常。
  • 如果成员用 @property@synthsize 处理,因为 @synthsize 告诉编译器自动生成 set<Key>: 格式的方法,所以这种情况下会直接搜索到。

valueForKey: 的查找方式:

  • 查找以 get<Key><key>is<Key> 命名规则查找方法,找到直接调用。
  • 如果没有找到,则会查找 countOf<Key>objectIn<Key>AtIndex:<Key>AtIndexes 格式的方法,找到就会调用 countOf<Key>objectIn<Key>AtIndex:<Key>AtIndexes 方法,还有一个可选的 get<Key>:range: 方法。
  • 若是还没查到,那么查找 countOf<Key>enumeratorOf<Key>memberOf<Key>: 格式的方法,如果找到就调用 countOf<Key>enumeratorOf<Key>memberOf<Key>: 方法。
  • 若是还没查到,那么查看类方法 accessInstanceVariablesDirectly 的返回值。如果返回 YES,那么将在对象内部查找名为 _<key>_is<Key><key>is<key> 的实例变量,如果找到则设置成员的值。
  • 如果没有查找调用 valueForUndefinedKey:,并抛出异常。

综上,使用 KVC 访问属性的代价比直接使用存取方法性能开销要大

值的正确性核查

在调用 KVC 时可以先进行验证,验证通过下面两个方法进行,支持 keykeyPath 两种方式。验证方法默认实现返回 YES,可以通过重写对应的方法修改验证逻辑,验证方法需要我们手动调用,并不会在进行 KVC 的过程中自动调用。

实现核查方法,为如下格式:validate<Key>:error:

- (BOOL)validateName:(id *)ioValue error:(NSError **)outError {
    // The name must not be nil, and must be at least two characters long.
    if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2]) {
        if (outError != NULL) {
            NSString *errorString = NSLocalizedStringFromTable(
                    @"A Person's name must be at least two characters long", @"Person",
                    @"validation: too short name error");
            NSDictionary *userInfoDict =
                [NSDictionary dictionaryWithObject:errorString
                                            forKey:NSLocalizedDescriptionKey];
            *outError = [[[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN
                                                    code:PERSON_INVALID_NAME_CODE
                                                userInfo:userInfoDict] autorelease];
        }
        return NO;
    }
    return YES;
}

调用核查方法:
validateValue:forKey:error:,默认实现会搜索 validate<Key>:error:格式的核查方法,找到则调用,未找到默认返回 YES
validateValue 方法的内部实现中,如果传入的 valuekey 有问题,可以通过返回 NO 来表示错误,并设置 NSError 对象。

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

推荐阅读更多精彩内容

  • KVC KVC定义 KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过K...
    暮年古稀ZC阅读 2,140评论 2 9
  • 关于键值编码 键值编码(KVC)是一种由NSKeyValueCoding非正式协议提供的机制,对象采用该机制来提供...
    渐z阅读 920评论 0 0
  • KVC(Key-valuecoding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS...
    榕樹頭阅读 703评论 0 2
  • KVC KVC定义 KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过K...
    jackyshan阅读 51,872评论 9 200
  • KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iO...
    黑暗中的孤影阅读 49,697评论 74 441