OC底层原理16-KVC底层原理

iOS--OC底层原理文章汇总

KVC(Key Value Coding)是定义在NSKeyValueCoding.h文件中的一个非正式协议。KVC提供了一种可以间接访问其属性方法或成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。

常用API

设值与取值

常用到的设值与取值的几种方式:

// 通过Key来取值
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;

// 通过KeyPath(也称路由)来取值
- (nullable id)valueForKeyPath:(NSString *)keyPath; 
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  
KeyPath

使用示例入

    [myAcount setValue:@"xiaoxiao" forKeyPath:@"model.name"]; // 可以通过 . 的形式获取到更深层的属性  
    NSLog(@"%@",[myAcount valueForKeyPath:@"model.name"]);
字典操作
- (void)dictionaryTest{
    NSDictionary* dict = @{
                           @"name":@"xiaoxiao",
                           @"age":@18,
                           };
    PeasonModel  *p = [[PeasonModel alloc] init];
    // 字典转模型
    [p setValuesForKeysWithDictionary:dict];
    NSLog(@"%@",p);
    // 键数组转模型到字典
    NSArray *array = @[@"name",@"age"];
    NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
    NSLog(@"%@",dic);
}
其他常用方法
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly;

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

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

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

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

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

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

设值&取值过程

1.设值

常用设值方法就是setValue:forKey,其底层原理是怎么样的呢?开发过程中,我们可能曾经试着Jump to Definition去查看底层实现,然后只有注释,Foundation闭源的。想要窥探其底层,可能需要其他手段:1.通过苹果官方文档;2.Github搜相关的工程;3.通过Hopper反汇编,通过伪代码去理解;

官网Key-Value Coding
在官网中找到了以下关于setValue:forkey的解释

setValue:forkey

执行流程是:

  • 1.先寻找是否有set<Key> or _set<Key>,如果找到,就直接设置属性value.(key是指成员变量名,首字符大小写需要符合KVC的命名规范));
  • 2.如果没找到,就判断accessInstanceVariablesDirectly返回YES,就按照以下顺序寻找一个实例变量,名称类似_<key>,_is<Key>,<key>,或者is<Key>,找到就直接赋值;
  • 3.如果返回为NO,则表明setter方法 或者 实例变量都没有找到,系统会执行该对象的setValue:forUndefinedKey:方法,默认会抛出NSUndefinedKeyException类型的异常。

整理得到流程图更方便理解


setValue:forKey: 流程
2.取值

当然也就找到了valueForKey:的解释

取值-valueForKey 逻辑

流程是:

  • 1.先找getter方法,按照顺序依次查找:get<Key> -> <key> -> is<Key> -> _<key>,如果找到执行第5,否者执行第2;
  • 2.按顺序次没找着,就会寻找countOf <Key>和objectIn <Key> AtIndex :和<key> AtIndexes :, 如果找到了countOf <Key>或其他两个中的一个,则会创建一个响应所有NSArray方法的集合代理对象,并返回该对象,即NSKeyValueArray,是NSArray的子类。否则执行第3.
    代理对象随后将接收到的所有NSArray消息转换为countOf<Key>objectIn<Key> AtIndex:<key>AtIndexes:消息的某种组合,用来创建键值编码对象。如果原始对象还实现了一个名为get<Key>:range:之类的可选方法,则代理对象也将在适当时使用该方法;
  • 3.上面方法没找到,就会查找countOf <Key>,enumeratorOf<Key>和memberOf<Key>这三个方法;
    如果找到所有的三个方法,则会创建一个响应所有NSSet方法的集合代理对象并将其返回。否则,请继续执行步骤4。
    此代理对象随后将其收到的所有NSSet消息转换为countOf <Key>,enumeratorOf <Key>和memberOf <Key>:消息的某种组合,以创建它的对象。实际上,代理对象与与键值编码兼容的对象一起工作,使基础属性的行为就好像它是NSSet一样,即使它不是NSSet那样。
  • 4.如果没有找到方法组,并且接收方的类方法accessInstanceVariablesDirectly返回YES,则按顺序搜索名为_ <key>,_ is <Key>,<key>或is <Key>的实例变量。如果找到,请直接获取实例变量的值,然后继续执行步骤5。否则,请继续执行步骤6。
  • 5.如果搜索到的属性值的类型为指针,则直接返回结果;
    如果该值是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回它。
    如果结果是NSNumber不支持的标量类型,则转换为NSValue对象并返回该对象。
    如果所有其他方法均失败,则调用valueForUndefinedKey:。默认情况下,这会引发一个异常NSUndefinedKeyException

整理得到流程图:


valueForKey: 取值流程

KVC应用场景

  • 1、动态设值和取值
    常用通过setValue:forKey:valueForKey:, 或者setValue:forKeyPath:valueForKeyPath:

  • 2、通过KVC访问和修改私有变量
    在外部定义的对象是无法直接访问私有属性的,但是可以通过KVC修改和访问任何私有属性

  • 3、多值操作(Model和字典互转)

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