iOS KVC

一、简介

KVC是KeyValue Coding的简称,它是对NSObject的扩展NSKeyValueCoding,我们可以通过字符串的名字(key)来获取和设置属性的机制

二、普通对象&不可变容器

下面介绍一下几个方法,这几个方法的调用过程在 官方文档 有详细的说明。XCode(8.3)的 NSKeyValueCoding.h 头文件的注释中也有说明,我下边绘制了几张流程图

1. valueForKey:

为了使流程图更加清晰,这里没有列出全部的方法,有兴趣,可以查阅下文档,里边说的很详细,最好对照文档看,以便查看没有列出的方法以及详细的调用逻辑

valueForKey: 数据查找过程.png
2. setValue: forKey:
setValue: forKey: 数据设置过程.png

三、可变容器

1. mutableArrayValueForKey:

1.调用方法与调用 -valueForKey: 方法值的查找过程其实是类似的,但是它返回了一个集合代理对象,我们调用集合代理对象的insert或romove等方法就会触发如下图所示的处理过程。

2.这里并没有列出全部的插入,移除方法。文档里也详细说明实现至少一个插入方法和至少一个移除方法(文档里的意思应该是对应的插入移除方法要成对出现),调用 mutableArrayValueForKey: 才会返回集合代理对象

3.此外还有 mutableOrderedSetValueForKey: 与 mutableSetValueForKey: 两个方法,它们分别对有序可变集合与无序可变集合的操作,调用过程与 mutableArrayValueForKey: ;类似,不再详述

mutableArrayValueForKey: 数据查找过程.png

测试样例:

//  KYSKVCMutableArrayTest.h

@interface KYSKVCMutableArrayTest : NSObject

@property (strong ,nonatomic) NSMutableArray<NSString *> *kys;

- (void)insertObject:(id)object inKysAtIndex:(NSUInteger)index;

- (void)removeObjectFromKysAtIndex:(NSUInteger)index;

-(void)addObjectObserverWithObject:(NSString *)str;

-(void)removeLastObjectObserver;

@end

//  KYSKVCMutableArrayTest.m

@implementation KYSKVCMutableArrayTest

-(id)init{
    if (self == [super init]){
        _kys = [NSMutableArray new];
        [self addObserver:self forKeyPath:@"kys" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        
    }
    return self;
}

-(void)dealloc{
    [self removeObserver:self forKeyPath:@"kys"];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"收到数组变化通知:%@",change);
}

- (void)insertObject:(id)object inKysAtIndex:(NSUInteger)index {
    NSLog(@"insertObject:inKysAtIndex:%@",@(index));
    [self.kys insertObject:object atIndex:index];
}
- (void)removeObjectFromKysAtIndex:(NSUInteger)index {
    NSLog(@"removeObjectFromKysAtIndex:%@",@(index));
    [self.kys removeObjectAtIndex:index];
}

-(void)addObjectObserverWithObject:(NSString *)str{
    [[self mutableArrayValueForKey:@"kys"] addObject:str];
}

-(void)removeLastObjectObserver{
    [[self mutableArrayValueForKey:@"kys"] removeLastObject];
}

@end

测试


KYSKVCMutableArrayTest *test = [[KYSKVCMutableArrayTest alloc] init];
    
NSMutableArray *mArray = [test mutableArrayValueForKey:@"kys"];

[mArray insertObject:@"000" atIndex:0];
    
[mArray removeObjectAtIndex:0];

打印结果

打印结果.png

利用这个特性我们可以实现对数组变化的监听(KVO)


    KYSKVCMutableArrayTest *test = [[KYSKVCMutableArrayTest alloc] init];
    
    [test addObjectObserverWithObject:@"111"];
    
    [test removeLastObjectObserver];

打印结果

数组变化的监听.png

四、其他方法

  • dictionaryWithValuesForKeys: 传入一组key,返回这组key与value的字典
  • setValuesForKeysWithDictionary:可以通过字典设置一组值
    //这里用字典进行测试,使用一个自定义的模型类也是一样的
    NSMutableDictionary *mDic=[@{@"id":@"001",
                                 @"name":@"kys",
                                 @"des":@"adhkaslkfdladf"} mutableCopy];
    
    NSLog(@"%@",[mDic dictionaryWithValuesForKeys:@[@"id",@"name"]]);
    
    [mDic setValuesForKeysWithDictionary:@{@"id":@"002",@"name":@"kys2"}];
    
    NSLog(@"%@",mDic);

打印结果

  • valueForKeyPath:其实就是先用“.”把各个key提取出来依次查找
  • setValue: forKeyPath: 先用“.”把各个key提取出来,查找到最后一层进行赋值

五、检查正确性

我们可以通过以下两个方法验证设置的值是否正确,如果不正确可以在这两个方法里做出相应的处理

  • validateValue: forKey: error:
  • validateValue: forKeyPath: error:

六、集合操作

这些操作其实是很方便的 官方文档 有相关的样例

1. 常规操作符
  • @sum:值的总和
  • @avg:平均值
  • @count:总个数
  • @max:最大值
  • @min:最小值

NOTE:由于是KeyPath可以这样调用@"@sum.floatValue"

    KYSKVCTest *test1=[[KYSKVCTest alloc] init];
    test1.num=1;
    KYSKVCTest *test2=[[KYSKVCTest alloc] init];
    test2.num=2;
    KYSKVCTest *test3=[[KYSKVCTest alloc] init];
    test3.num=3;
    
    NSArray *array=@[test1,test2,test3];
    NSNumber* sum = [array valueForKeyPath:@"@sum.num"];
    NSNumber* avg = [array valueForKeyPath:@"@avg.num"];
    NSNumber* count = [array valueForKeyPath:@"@count"];
    NSNumber* min = [array valueForKeyPath:@"@min.num"];
    NSNumber* max = [array valueForKeyPath:@"@max.num"];
    
    NSLog(@"sum:%@, avg:%@, count:%@, min:%@, max:%@",sum,avg,count,min,max);
    
    //打印结果:sum:6, avg:2, count:3, min:1, max:3
2. 对象操作
  • @distinctUnionOfObjects:元素唯一,会进行去重
  • @unionOfObjects:不会去重
    KYSKVCTest *test1=[[KYSKVCTest alloc] init];
    test1.num=1;
    KYSKVCTest *test2=[[KYSKVCTest alloc] init];
    test2.num=2;
    KYSKVCTest *test3=[[KYSKVCTest alloc] init];
    test3.num=3;
    KYSKVCTest *test4=[[KYSKVCTest alloc] init];
    test4.num=2;
    
    NSArray *array=@[test1,test2,test3,test4];
    
    NSArray *distinctUnionOfObjectsArray=[array valueForKeyPath:@"@distinctUnionOfObjects.num"];
    NSLog(@"distinctUnionOfObjects:%@",distinctUnionOfObjectsArray);
    
    NSArray *unionOfObjectsArray=[array valueForKeyPath:@"@unionOfObjects.num"];
    NSLog(@"unionOfObjects:%@",unionOfObjectsArray);
    
    /* 打印结果
     distinctUnionOfObjects:(
        3,
        2,
        1
     )
     unionOfObjects:(
        1,
        2,
        3,
        2
     )
     */
3. 集合操作
  • @distinctUnionOfArrays:去重
  • @unionOfArrays:不去重
  • @distinctUnionOfSets:集合无重复的

array与set类似,这里只给出array的样例

    KYSKVCTest *test1=[[KYSKVCTest alloc] init];
    test1.num=1;
    KYSKVCTest *test2=[[KYSKVCTest alloc] init];
    test2.num=2;
    KYSKVCTest *test3=[[KYSKVCTest alloc] init];
    test3.num=3;
    KYSKVCTest *test4=[[KYSKVCTest alloc] init];
    test4.num=2;
    NSArray *array1=@[test1,test2,test3,test4];
    
    KYSKVCTest *test5=[[KYSKVCTest alloc] init];
    test5.num=5;
    KYSKVCTest *test6=[[KYSKVCTest alloc] init];
    test6.num=6;
    KYSKVCTest *test7=[[KYSKVCTest alloc] init];
    test7.num=6;
    KYSKVCTest *test8=[[KYSKVCTest alloc] init];
    test8.num=7;
    
    
    KYSKVCTest *test9=[[KYSKVCTest alloc] init];
    test9.num=9;
    KYSKVCTest *test10=[[KYSKVCTest alloc] init];
    test10.num=9;
    KYSKVCTest *test11=[[KYSKVCTest alloc] init];
    test11.num=10;
    //注意这里的嵌套
    NSArray *array2=@[test5,test6,test7,test8,@[test9,test10,test11]];
    
    NSArray *array=@[array1,array2];
    NSArray *distinctUnionOfArraysArray=[array valueForKeyPath:@"@distinctUnionOfArrays.num"];
    NSLog(@"distinctUnionOfArrays:%@",distinctUnionOfArraysArray);
    NSArray *unionOfArraysArray=[array valueForKeyPath:@"@unionOfArrays.num"];
    NSLog(@"unionOfArrays:%@",unionOfArraysArray);
    
    /* 打印结果
     distinctUnionOfArrays:(
        5,
        1,
        (
            9,
            9,
            10
        ),
        6,
        2,
        7,
        3
     )
     unionOfArrays:(
        1,
        2,
        3,
        2,
        5,
        6,
        6,
        7,
        (
            9,
            9,
            10
        )
     )
     */

七、实现方式

运用isa-swizzling技术,通过isa-swizzling来实现其内部定位查找

八、总结

之前在SDWebImage的源码里,看见直接对数组调用valueForKey:方法,当时不明白什么意思,然后查阅了一下相关的文档,发现很多方法都很有用,可以使我们的代码更加精简。如果文章里哪里有不对的地方,欢迎指出_!

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

推荐阅读更多精彩内容

  • 什么是KVC? KVC的全称叫Key-Value Coding,也叫做键值编码,在apple官方文档中是这么解释的...
    Joker_King阅读 649评论 0 3
  • 关于KVC的详细介绍可以参考官方文档 键值编码(KVC)是由NSKeyValueCoding非正式协议启用的一种机...
    你duck不必呀阅读 628评论 0 1
  • 什么是KVC? KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实是指iOS的开...
    萨缪阅读 809评论 0 5
  • 什么是KVC? KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实是指iOS的开...
    萨缪阅读 4,627评论 1 13
  • 前言:往往会某项工具WORK,就想究其原理。本文先简单介绍KVC 一、KVC 简介 1.1 KVC 概述 1.KV...
    梦蕊dream阅读 906评论 0 2