第二十节—KVC(一)

2020-11-08  本文已影响0人  L_Ares

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

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

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

kvc文档.png

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

一、KVC基本简介

1. KVC的定义

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

+ (BOOL)accessInstanceVariablesDirectly;

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

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
- (void)setNilValueForKey:(NSString *)key;
- (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);
}
上一篇 下一篇

猜你喜欢

热点阅读