美文网首页
第二十节—KVC(一)

第二十节—KVC(一)

作者: L_Ares | 来源:发表于2020-11-08 16:24 被阅读0次

本文为L_Ares个人写作,以任何形式转载请表明原文出处。

网上可以搜索到的KVC文章太多了,之所以都这么喜欢研究这个东西,是因为KVC的作用域之广泛,只要你还需要用到对象这个概念,怕是都有可能要碰到KVC帮你解决问题。

本节将从苹果官方文档来进入KVC。不再使用源码是因为KVC的源码在Foundation框架中,找不到其开源的源码。

kvc文档.png

准备工作 : 苹果官方文档。必须要有这个。进入之后搜索栏自行搜索key value coding,不要搜索kvc这里直达KVC

一、KVC基本简介

1. KVC的定义

  • 英文全称 : key value coding(就是准备工作里面让大家搜的)

  • 中文全称 : 键值编码

  • 主要功能 :

    • OC的私有变量变成了单一名词 : 通过变量名称字符串直接访问成员变量,无论公有还是私有
    • 践行了OC的动态性 : 无需调用明确的存取方法动态的访问和修改对象属性。
  • 依赖性 : 根据KVC的主要功能就能知道,依赖的是Runtime

2. KVC的简介

下面这些都是从官方文档里面根据自身的一些了解翻译的,有不当之处,还请指出,感激不尽。

(1). KVC本身是一种机制,它是根据NSKeyValueCoding非正式协议使用的。对象采用NSKeyValueCoding非正式协议可以对其属性进行访问。
(2). 当一个对象兼容键值对编码(kvc)时,可以通过简洁、统一的消息传递接口利用字符串参数对它的属性进行访问。
(3). KVC这种间接的访问机制,提供了一种直接访问实例变量和它们的settetgetter的方式。

我们经常使用setter或者getter来访问对象的属性。大家也清楚setter可以给属性赋值,getter可以返回属性的值。在OC中,你还可以直接访问属性的底层的实例变量。

使用上述的任何一种方式访问对象的属性都很简单,但是都需要调用属性自己的,特定的方法或者变量名。而且随着属性列表的增加或者更改,访问属性的代码也需要增加或者更改。KVC就提供了一种更简单的,更统一的消息传递接口,对所有遵循KVC机制的属性都可以适用。

KVC是其他很多Cocoa技术的基础概念,比如说键值观察(KVO)Cocoa BindingCoreDataAppleScript(写mac脚本的)。在某些情况下,KVC还可以帮我们简化一些代码。

到这里,大体对KVC也算有一个官方的理解了。这个模块的一些其他内容就不翻译了,后面有时间再翻译一下,也可以去网上搜索,有很多的小伙伴也写过的,就不赘述了,下面直接上第二个模块的内容,用代码来直接实现官方的一些规定。

二、KVC中常见API

1. KVC设置值

1.1 key设置value
- (void)setValue:(nullable id)value forKey:(NSString *)key;
1.2 keyPath设置value
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

2. KVC取值

2.1 通过key取value
- (nullable id)valueForKey:(NSString *)key;
2.2 通过keyPath取value
- (nullable id)valueForKeyPath:(NSString *)keyPath; 

3. 其他常见API

  • 对实例变量而言(不对属性),开启或者关闭实例变量赋值。默认返回YES,开启。
+ (BOOL)accessInstanceVariablesDirectly;

大家都知道,实例变量和属性不同,是不默认生成setter方法的,如果不开启这个,又不自己给实例变量添加set方法的话,那么就不可以直接给实例变量进行赋值。

  • 检查给keyvalue是否是有效的。
    也就是说,可以验证给指定的keyset的值是否正确。如果不正确,可以替换或者拒绝,并且可以给error的地址上添加错误的原因。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
  • 属性是NSMutableArray类型,通过key获取该属性。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
  • 无法识别的key
    key不存在,且KVC找不到任何与key的字符串相关的字段或属性,会调用到这里,默认是抛出异常。
- (nullable id)valueForUndefinedKey:(NSString *)key;
  • 对无法识别的key赋值
    key不存在,KVC也找不到任何与key的字符串相关的字段或属性,无法对key进行设置value,会调用到这里,默认抛出异常。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
  • 设置keyvaluenil
    如果给一个已知的key设置value = nil;那么就会走到这里。
- (void)setNilValueForKey:(NSString *)key;
  • 根据一组key找到value然后转成NSDictionary
    该方法可以用于模型转字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

三、一些简单的KVC使用

准备 : 创建一个Project--->App。创建一个继承于NSObjectJDPerson类。再创建一个继承于NSObjectJDMan类。

JDPerson.h :

#import <Foundation/Foundation.h>
#import "JDMan.h"

NS_ASSUME_NONNULL_BEGIN

typedef struct {
    float x,y,z;
}ThreeFloats;

@interface JDPerson : NSObject

{
    @public
    NSString *myName;
}

@property (nonatomic, copy)   NSString         *name;

@property (nonatomic, strong) NSArray          *array;

@property (nonatomic, strong) NSMutableArray   *mutArr;

@property (nonatomic, assign) int              age;

@property (nonatomic)         ThreeFloats      threeFloats;

@property (nonatomic, strong) JDMan            *man;

@end

NS_ASSUME_NONNULL_END

JDMan.h :

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface JDMan : NSObject

@property (nonatomic, copy) NSString         *name;

@property (nonatomic, copy) NSString         *work;

@property (nonatomic, copy) NSString         *hobby;

@property (nonatomic, assign) int            age;

@property (nonatomic, assign) int            height;

@property (nonatomic, strong) NSMutableArray *bookArr;

@end

NS_ASSUME_NONNULL_END

ViewController中引入JDPerson.hJDMan.h。准备工作完成。

KVC中常见的使用

1 基本类型设置值
#pragma mark - KVC基本类型赋值与取值
- (void)jd_kvc_basicType
{
    /**
     普通的setter方法对对象进行赋值
     */
    JDPerson *person = [[JDPerson alloc]init];
    person.name    = @"JD";
    person.age     = 18;
    person->myName = @"LJD";
    NSLog(@"setter方法 : %@ - %d - %@",person.name,person.age,person->myName);
    
    /**
     1. KVC : 基本类型
     */
    [person setValue:@"JD_KVC" forKey:@"name"];
    [person setValue:@16 forKey:@"age"];
    [person setValue:@"LJD_JVC" forKey:@"myName"];
    NSLog(@"KVC : 基本类型 : %@ - %@ - %@",[person valueForKey:@"name"],
                                          [person valueForKey:@"age"],
                                          [person valueForKey:@"myName"]);
}

2 集合类型修改值
#pragma mark - KVC集合类型修改值
- (void)jd_kvc_collectionTypes
{
    /**
     2. KVC : 集合类型
        修改集合中的第一个元素@"1"
        由于array是不可变数组,不可以直接进行修改
     */
    JDPerson *person = [[JDPerson alloc]init];
    person.array = @[@"1",@"2",@"3"];
    //普通KVC方式
    NSArray *array = [person valueForKey:@"array"];
    array = @[@"666",@"2",@"3"];
    [person setValue:array forKey:@"array"];
    NSLog(@"集合类型-普通KVC方式 : %@",[person valueForKey:@"array"]);
    //KVC API方式
    NSMutableArray *mutArr = [person mutableArrayValueForKey:@"array"];
    mutArr[0] = @"888";
    NSLog(@"集合类型-KVC API方式 : %@",[person valueForKey:@"array"]);
    
}
3 集合操作符

这里包含一些KVC特殊的操作符,比如lowercaseString(小写)、@avg(平均数)、@count(数量)、@sum(求和)、@max(最大值)、@min(最小值)、@unionOfObjects(相同的key的value)、@distinctUnionOfObjects(相同的key的value并且去重)等等。

直接上代码 :

#pragma mark - KVC字典操作
- (void)jd_kvc_dictionary
{
    NSDictionary *dic = @{
        @"name"   : @"ljd",
        @"work"   : @"coder",
        @"age"    : @18,
        @"height" : @150,
        @"hobby"  : @"study"
    };
    
    JDMan *man = [[JDMan alloc] init];
    //普通的字典转模型
    [man setValuesForKeysWithDictionary:dic];
    NSLog(@"KVC字典操作-字典转模型 : %@-%@-%d-%d-%@",man.name,man.work,man.age,man.height,man.hobby);
    //通过key的数组转模型到字典
    NSArray *keyArray = @[@"name",@"work"];
    NSDictionary *dict = [man dictionaryWithValuesForKeys:keyArray];
    NSLog(@"KVC字典操作-通过key的数组转模型到字典 : %@",dict);
    
}

#pragma mark - KVC消息传递
- (void)jd_kvc_messagePass
{
    //消息从tempArray传递到了string
    NSArray *tempArray = @[@"Apple",@"Banana",@"Grapes",@"Peach"];
    NSArray *lengthArr = [tempArray valueForKeyPath:@"length"];
    NSLog(@"KVC消息传递-length : %@",lengthArr);
    NSArray *lowStrArr = [tempArray valueForKeyPath:@"lowercaseString"];
    NSLog(@"KVC消息传递-小写 : %@",lowStrArr);
}

#pragma mark - KVC聚合操作符
- (void)jd_kvc_aggregation_operator
{
    NSMutableArray *manArr = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        JDMan *man = [JDMan new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"work"  : @"coder",
            @"hobby" : @"study",
            @"age"   : @(18 + i),
            @"height": @(175 + 2 * arc4random_uniform(6))
        };
        [man setValuesForKeysWithDictionary:dic];
        [manArr addObject:man];
    }
    //取对象中的`height`,结果是一个数组
    NSLog(@"KVC聚合操作符-height : %@",[manArr valueForKey:@"height"]);
    //取`height`平均数
    float avg_length = [[manArr valueForKeyPath:@"@avg.height"] floatValue];
    NSLog(@"KVC聚合操作符-平均数 : %f",avg_length);
    //`height`的数量
    int count = [[manArr valueForKeyPath:@"@count.height"] intValue];
    NSLog(@"KVC聚合操作符-属性数量 : %d",count);
    //求和
    int sum = [[manArr valueForKeyPath:@"@sum.height"] intValue];
    NSLog(@"KVC聚合操作符-求和 : %d",sum);
    //最大值
    int max = [[manArr valueForKeyPath:@"@max.height"] intValue];
    NSLog(@"KVC聚合操作符-最大值 : %d",max);
    //最小值
    int min = [[manArr valueForKeyPath:@"@min.height"] intValue];
    NSLog(@"KVC聚合操作符-最小值 : %d",min);
    
}

#pragma mark - KVC数组操作符
- (void)jd_kvc_array_operator
{
    NSMutableArray *manArr = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        JDMan *man = [JDMan new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"work"  : @"coder",
            @"hobby" : @"study",
            @"age"   : @(18 + i),
            @"height": @(175 + 2 * arc4random_uniform(6))
        };
        [man setValuesForKeysWithDictionary:dic];
        [manArr addObject:man];
    }
    
    //取数组中的对象元素的某一属性,然后形成对象属性数组
    //通过key取
    NSLog(@"KVC数组操作符-key取属性 : %@",[manArr valueForKey:@"height"]);
    //通过keyPath取
    NSLog(@"KVC数组操作符-keyPath取属性 : %@",[manArr valueForKeyPath:@"@unionOfObjects.height"]);
    //通过keyPath取,并且属性去重
    NSLog(@"KVC数组操作符-keyPath取属性且去重 : %@",[manArr valueForKeyPath:@"@distinctUnionOfObjects.height"]);
    
}

#pragma mark - KVC嵌套数组操作符
- (void)jd_kvc_nested_array_operator
{
    NSMutableArray *manArr = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        JDMan *man = [JDMan new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"work"  : @"coder",
            @"hobby" : @"study",
            @"age"   : @(18 + arc4random_uniform(6)),
            @"height": @(175 + i)
        };
        [man setValuesForKeysWithDictionary:dic];
        [manArr addObject:man];
    }
    
    NSMutableArray *personArr = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        JDPerson *person = [JDPerson new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"age"   : @(18 + arc4random_uniform(6)),
        };
        [person setValuesForKeysWithDictionary:dic];
        [personArr addObject:person];
    }
    
    //嵌套数组
    NSArray *nestArr = @[manArr,personArr];
    
    //取嵌套数组的两个数组中对象元素的某一相同属性,然后形成对象属性数组
    NSLog(@"KVC嵌套数组操作符 - %@",[nestArr valueForKeyPath:@"@unionOfArrays.age"]);
    
    //去重
    NSLog(@"KVC嵌套数组操作符-去重 - %@",[nestArr valueForKeyPath:@"@distinctUnionOfArrays.age"]);
    
}

#pragma mark - KVC嵌套集合
- (void)jd_kvc_nested_set
{
    NSMutableSet *manSet = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        JDMan *man = [JDMan new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"work"  : @"coder",
            @"hobby" : @"study",
            @"age"   : @(18 + arc4random_uniform(6)),
            @"height": @(175 + i)
        };
        [man setValuesForKeysWithDictionary:dic];
        [manSet addObject:man];
    }
    
    NSMutableSet *personSet = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        JDPerson *person = [JDPerson new];
        NSDictionary *dic = @{
            @"name"  : @"JD",
            @"age"   : @(18 + arc4random_uniform(6)),
        };
        [person setValuesForKeysWithDictionary:dic];
        [personSet addObject:person];
    }
    
    //嵌套Set
    NSSet *nestSet = [NSSet setWithObjects:manSet, personSet, nil];
    NSLog(@"KVC嵌套集合-交集 - %@",[nestSet valueForKeyPath:@"@distinctUnionOfSets.age"]);
    
}
4 访问非对象属性

非对象属性,就像那些常见的基本类型,还有结构体之类的。这里主要说一下结构体。因为有的人会利用这种方法做C/C++OC的混编。

这种基本数据类型或者数据结构是不可以直接当value使用的,要根据其类型转换成NSValue或者其他的OC类。

#pragma mark - KVC访问非对象属性
- (void)jd_kvc_non_object_attribute
{
    JDPerson *person = [[JDPerson alloc] init];
    ThreeFloats tfValue = {1.f,2.f,3.f};
    NSValue *value = [NSValue valueWithBytes:&tfValue objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];
    
    NSValue *resultValue = [person valueForKey:@"threeFloats"];
    
    NSLog(@"KVC访问非对象属性-结构体 - %@",resultValue);
    
    ThreeFloats three_value;
    [resultValue getValue:&three_value];
    NSLog(@"KVC访问非对象属性-打印 - %f-%f-%f",three_value.x,three_value.y,three_value.z);
    
    
}
5 层层访问或者说对象嵌套

就是说对象的属性还是个对象。通过keyPath就可以了。

#pragma mark - KVC 对象中的对象
- (void)jd_kvc_obj_include_obj
{
    JDPerson *person = [[JDPerson alloc] init];
    JDMan *man = [[JDMan alloc] init];
    man.name = @"LJD";
    person.man = man;
    NSLog(@"KVC 对象中的对象-未改变 - %@",person.man.name);
    //利用kvc的keyPath再给person的man中的name赋值
    [person setValue:@"JD" forKeyPath:@"man.name"];
    NSLog(@"KVC 对象中的对象-改变后 - %@---%@",[person valueForKeyPath:@"man.name"],man.name);
}

相关文章

  • 第二十节—KVC(一)

    本文为L_Ares个人写作,以任何形式转载请表明原文出处。 网上可以搜索到的KVC文章太多了,之所以都这么喜欢研究...

  • 21/40灵魂之歌第20节

    21/40•第二十节 当恶魔啃食你的脚跟时,第二十节扫除你所有的罪恶。 第20节全文: Bharī-ai hath...

  • [KVC系列]底层执行流程

    KVC系列-底层执行流程 相当于是对KVC官方文档第二部分的一个总结 Search Pattern for the...

  • iOS原理篇(二): KVC实现原理

    KVC实现原理 什么是 KVC KVC基本使用 KVC 原理 总结 一 、 什么是KVC KVC的全称是Key-V...

  • OC语法:KVC的底层实现

    一、KVC是什么二、怎么使用KVC三、KVC的底层实现四、KVC常见面试题 一、KVC是什么 KVC全称Key-V...

  • iOS 关于KVC的一些总结(转)

    原文:iOS 关于KVC的一些总结 本文参考: KVC官方文档 KVC原理剖析 iOS KVC详解 KVC 简介 ...

  • 第二十节

    杨庆丰在学校的楼道里碰到了温会军,秀气的小伙子多了几份俊郎,见着杨庆丰他竟有点不好意思,略略打过招呼便跑掉了...

  • KVC详解

    KVC 目录结构KVC定义KVC取值和设置KVC使用keyPathKVC处理字典KVC作用 参考:iOS KVC和...

  • ios开发UI篇—Kvc简单介绍

    一、KVC简单介绍 KVC key valued coding 键值编码 KVC通过键值间接编码 补充: 与KVC...

  • iOS KVC和KVO详解

    一. KVC 1.KVC介绍 KVC 就是键值编码(key-value-coding)。 2.KVC 的主要作...

网友评论

      本文标题:第二十节—KVC(一)

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