iOS底层原理:KVC简析&自定义

简介

  • KVC全程Key-Value Coding
    键值编码是NSKeyValueCoding非正式协议支持的一种机制,对象采用这种非正式协议来提供对其属性的间接访问。当一个对象的键值编码是兼容的,它的属性可以通过一个简洁、统一的消息传递接口通过字符串参数寻址。这种间接访问机制补充了实例变量及其关联访问器方法提供的直接访问。

常用API

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

原理

  • 通过代码跟踪发现KVC相关API在Foundation框架中,而Foundation又不开源,所以我们只能通过反汇编获取官方文档的方式进行探索,下面我们通过官网文档探索KVC文档地址

取值原理

  • 1.首先查找get方法,查找顺序get, , is, or _,找到的话直接返回结果
  • 2.如果get方法没找到,会查找countOf 和objectIn AtIndex :和 AtIndexes :
    • 如果找到countOf 和其他两个中的一个,则会创建一个响应所有NSArray方法的集合代理对象,并返回该对象,即NSKeyValueArray,是NSArray的子类。代理对象随后将接收到的所有NSArray消息转换为countOf,objectIn AtIndex:和AtIndexes:消息的某种组合,用来创建键值编码对象。如果原始对象还实现了一个名为get:range:之类的可选方法,则代理对象也将在适当时使用该方法(注意:方法名的命名规则要符合KVC的标准命名方法,包括方法签名。)
  • 3.如果没有找到上面的方法,则会同时查找countOf enumeratorOf和memberOf这三个方法,如果这三个方法都找到,则会创建一个响应所有NSSet方法的集合代理对象,并返回该对象,此代理对象随后将其收到的所有NSSet消息转换为countOf,enumeratorOf和memberOf:消息的某种组合,用于创建它的对象
  • 4.这一步会先判断 accessInstanceVariablesDirectly是否返回YES,如果返回YES,继续往下查找,如果为NO,直接报错信息
  • 5.依次查找成员变量_, _is, , or is,找到的话返回结果,没找到报错信息

设值原理

  • 1.首先依次查找set方法 set:-> _set -> setIs
  • 2.判断accessInstanceVariablesDirectly是否返回YES,如果返回YES,继续依次查找成员变量_ -> _is -> -> is,如果为NO直接报错信息

自定义KVC

#import "NSObject+YX_KVC.h"
#import 
@implementation NSObject (YX_KVC)

- (id)yx_valueForKey:(NSString *)key
{
    if (key == nil || key.length == 0) {
        return nil;
    }
    
    NSString *Key = key.capitalizedString;
    NSString *getKey = [NSString stringWithFormat:@"get%@",Key];
    NSString *countOfKey = [NSString stringWithFormat:@"countOf%@",Key];
    NSString *objectInKeyAtIndex = [NSString stringWithFormat:@"objectIn%@AtIndex:",Key];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
        return [self performSelector:NSSelectorFromString(getKey)];
    }else if ([self respondsToSelector:NSSelectorFromString(key)]){
        return [self performSelector:NSSelectorFromString(key)];
    }else if ([self respondsToSelector:NSSelectorFromString(countOfKey)]){
        if ([self respondsToSelector:NSSelectorFromString(objectInKeyAtIndex)]) {
            int num = (int)[self performSelector:NSSelectorFromString(countOfKey)];
            NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1];
            for (int i = 0; i
  • 上述只是简单的KVC实现,如果想了解更多,可以看看这个反汇编实现

你可能感兴趣的:(iOS底层原理:KVC简析&自定义)