iOS KVC

一、简介

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

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

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

1. valueForKey:

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

iOS KVC_第1张图片
valueForKey: 数据查找过程.png
2. setValue: forKey:
iOS KVC_第2张图片
setValue: forKey: 数据设置过程.png

三、可变容器

1. mutableArrayValueForKey:

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

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

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

iOS KVC_第3张图片
mutableArrayValueForKey: 数据查找过程.png

测试样例:

//  KYSKVCMutableArrayTest.h

#import 

@interface KYSKVCMutableArrayTest : NSObject

@property (strong ,nonatomic) NSMutableArray *kys;

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

@end

//  KYSKVCMutableArrayTest.m

#import "KYSKVCMutableArrayTest.h"

@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 *)change context:(void *)context{
    NSLog(@"收到数组变化通知:%@",change);
}

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

@end

打印结果


iOS KVC_第4张图片
打印结果

从样例还可以看出,在代理对象上修改数组可以出发KVO,直接修改数组并不会出发KVO

四、其他方法

  • 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);

打印结果

iOS KVC_第5张图片
  • 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:方法,当时不明白什么意思,然后查阅了一下相关的文档,发现很多方法都很有用,可以使我们的代码更加精简。如果文章里哪里有不对的地方,欢迎指出_

你可能感兴趣的:(iOS KVC)