美文网首页
KVC的底层实现

KVC的底层实现

作者: 8fe8946fa366 | 来源:发表于2018-03-20 15:10 被阅读99次

    1.kvc的基本用法

        其实kvc是一个用起来非常简单的东西,并没有想象的那么复杂,他的作用就是根据属性的名字(key值)给一个属性赋值,或者是根据属性名(key值)给获取属性值。

    赋值:

    - (void)setValue:(nullable id)value forKey:(NSString *)key;

    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

    取值:

    - (nullable id)valueForKeyPath:(NSString *)keyPath; 

    - (nullable id)valueForKey:(NSString *)key;

    keyPath和key的区别是什么?

    ❓比如说有一个类叫做address,address这个类里有一个属性叫做country,我们可以直接通过setValueForKeyPath:@"address.country"这个方法给country属性赋值,而不需要先把address取出来再用setValueForKey方法给country赋值。

    2.kvc的好处

    当我们用@property声明一个属性的时候,系统会自动生成成员变量+get方法+set方法,这个时候,可以用点语法(set、get方法)给属性赋值和取值。但是如果这个属性写在类的.m文件里,其他文件就无法通过点语法去取值和赋值。

    用kvo可以给类的私有属性或成员变量赋值

    3.kvc赋值的底层实现

    为什么kvc可以根据key值就能修改对应属性的值呢?

    当调用方法setValue:属性值 forKey:@”name“的时候,底层实现的顺序如下:

    3.1.优先调用setName方法

    3.2.1 如果没有找到set方法(如果不是kvc的话,其他类里找不到这个属性的set方法就没办法给他赋值了),会检查+ (BOOL)accessInstanceVariablesDirectly有没有返回yes,这个方法默认返回的是yes。如果这个手动把accessInstanceVariablesDirectly这个方法的返回值设置成no的话,系统就不会继续往下查找了,直接调用-(void)setValue:(id)value forUndefinedKey:(NSString *)key这个方法,如果重写了这个方法程序就不会崩溃,会执行这个方法,如果没有重写这个方法,程序就会崩溃。

    🙋🌰:

    @implementation numClass{

    NSString* isMarry;

    }

    这个成员变量没有set方法,如果这个时候accessInstanceVariablesDirectly这个方法的返回值设置成no,还不重写(void)setValue:(id)value forUndefinedKey:(NSString *)key这个方法,程序就会崩溃,如果重写了就不会崩溃。

    3.2.2正常情况下,是不会像上面那样做的,找不到setName方法系统就去找+ (BOOL)accessInstanceVariablesDirectly方法有没有返回yes,如果返回了yes的话,就去找有没有叫做_name的成员变量,只要有就可以赋值。

    3.3如果还是没有,就会去找有没有叫做_isName的成员变量。

    3.4如果还没有,就继续找有没有叫做name或isName的成员变量。⚠️:一定要大写,不大写找不到

    3.5如果上面的那些成员变量都没有找到,就调用(void)setValue:(id)value forUndefinedKey:(NSString *)key这个方法,如果不重写程序就崩了。

    4.kvc取值的底层实现

    当调用valueForKey:@”name“的代码时,取值的时候查找顺序和赋值不一样,按照如下顺序:

    4.1按照getName,name,isName的顺序先去调用name的get方法,如果找到了就会直接调用,不会再往后面查。

    4.2如果没找到,会按顺序查找方法countOfName、objectInNameAtIndex或NameAtIndexes格式的方法。按照这几个方法的实现给name赋值。

    4.3如果还没有找到,就会去检查+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),那么和先前的设值一样,会按_name,_isName,name,isName的顺序搜索成员变量名,这里不推荐这么做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了类方法+ (BOOL)accessInstanceVariablesDirectly返回NO的话,那么会直接调用valueForUndefinedKey:

    5.KVC处理非对象和自定义对象

    KVC在取值和赋值的时候,都是使用对象。

    valueForKey:这个方法,总是返回id类型的对象。如果原本的变量类型是值类型或者结构体,返回值会封装成NSNumber或者NSValue对象。开发者手动转化成原来的类型(比如说把NSNumber转成NSInteger)。

    setValue:forKey:方法传值的时候,必须把传递的值转化成对象才能传进去。

    6.KVC与容器类

    如果访问的容器是一个不可变容器,不可变有序容器(NSArray)或不可变无序容器(NSSet)可以用valueForKey:来取值。

    如果访问的是一个可变容器,比如NSMutableArray,用- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;方法取值。

    7.KVC与字典

    dictionaryWithValuesForKeys:NSArray

    🌰:Address* add = [Address new];

    add.country =@"China";

    add.province =@"Guang Dong";

    add.city =@"Shen Zhen";

    add.district =@"Nan Shan";

    NSArray* arr = @[@"country",@"province",@"city",@"district"];

    NSDictionary* dict = [add dictionaryWithValuesForKeys:arr]; //把对应key所有的属性全部取出来

    打印出来的dict就是这样的city = "Shen Zhen";  country = China;  district = "Nan Shan";  province = "Guang Dong";

    setValuesForKeysWithDictionary

    NSDictionary* modifyDict = @{@"country":@"USA",@"province":@"california",@"city":@"Los angle"};

    [add setValuesForKeysWithDictionary:modifyDict];//用key Value来修改Model的属性NSLog(@"country:%@ province:%@ city:%@",add.country,add.province,add.city);

    打印出来是这样的 country:USA province:california city:Los angle

    8.KVC在iOS开发中的应用场景

    8.1动态的取值和赋值

    8.2访问类的私有属性和成员变量

    8.3结合运行时,实现字典转模型

    8.4修改原生控件的一些属性,因为原生控件的api不可见,只有用kvc才能修改内部的属性。可以用runtime获取苹果没有开放的属性名。

    8.5KVC提供了一些简单的aggregation函数,就像数据库里一样。

    NSNumber* sum = [arrBooks valueForKeyPath:@"@sum.price"];

    NSNumber* avg = [arrBooks valueForKeyPath:@"@avg.price"];

    相关文章

      网友评论

          本文标题:KVC的底层实现

          本文链接:https://www.haomeiwen.com/subject/aawpqftx.html