美文网首页
KVC探讨-具体应用以及属性验证、异常处理(三)

KVC探讨-具体应用以及属性验证、异常处理(三)

作者: GitArtOS | 来源:发表于2020-03-01 23:02 被阅读0次

1.基本用法

直接对属性或者成员变量进行取值和赋值

    GTPerson *person = [[GTPerson alloc] init];
    [person setValue:@"GT" forKey:@"name"];
    [person setValue:@29 forKey:@"age"];
    [person setValue:@"GT" forKey:@"myName"];
    NSLog(@"%@ - %@ - %@",[person valueForKey:@"name"],[person valueForKey:@"age"],[person valueForKey:@"myName"]);

2. 操作集合类型

针对集合属性,可以直接通过mutableArrayValueForKey对对象的集合属性进行操作更改

    person.array = @[@"1",@"2",@"3"];
    // KVC 的方式
    NSMutableArray *ma = [person mutableArrayValueForKey:@"array"];
    ma[0] = @"100";
    NSLog(@"%@",[person valueForKey:@"array"]);

3. 访问非对象属性

3.1 标量属性
标量属性.png

如图所示,常用的基本数据类型需要在设置属性的时候包装成NSNumber类型,然后在读取值的时候使用各自对应的读取方法,如 double 类型的标量读取的时候使用 doubleValue;

3.2 结构体
结构体.png

针对结构体,KVC也可以直接操作,但是操作时候需要将结构体转成NSValue类型;结构体的话就需要转换成 NSValue类型,如上图所示。 除了NSPoint, NSRange, NSRect, 和 NSSize,对于自定义的结构体,也需要进行 NSValue的转换操作

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

    ThreeFloats floats = {1., 2., 3.};
    NSValue *value  = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];
    NSValue *reslut = [person valueForKey:@"threeFloats"];
    NSLog(@"%@",reslut);
    
    ThreeFloats th;
    [reslut getValue:&th] ;
    NSLog(@"%f - %f - %f",th.x,th.y,th.z);

4.嵌套访问

假如对象的属性也是对象,那么KVC可以通过keyPath来操作对象属性的属性

    GTStudent *student = [[GTStudent alloc] init];
    student.subject    = @"iOSGG";
    person.student     = student;
    [person setValue:@"GTOS" forKeyPath:@"student.subject"];
    NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);

5.集合操作符

5.1字典操作

假如字典的key和一个对象的属性都一样,那么可以通过setValuesForKeysWithDictionary直接将字典的value赋值给对象相应的属性,同样,也可以通过dictionaryWithValuesForKeys将对象转换成字典

- (void)dictionaryTest{
    
    NSDictionary* dict = @{
                           @"name":@"Cooci",
                           @"nick":@"GitArtOS",
                           @"subject":@"iOSGG",
                           @"age":@29,
                           @"length":@120
                           };
    GTStudent *p = [[GTStudent alloc] init];
    // 字典转模型
    [p setValuesForKeysWithDictionary:dict];
    NSLog(@"%@:%@",p,p.name);
    // 键数组转模型到字典
    NSArray *array = @[@"name",@"age"];
    NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
    NSLog(@"%@",dic);
}

5.2 操作数组元素的信息(KVC消息传递)

通过api可以拿到数组元素的长度,也可以对数组元素进行操作得到新的数组

NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
    //得到数组中所有元素的长度
    NSArray *lenStr= [array valueForKeyPath:@"length"];
    NSLog(@"%@",lenStr);
    //将数组中所有值全变成小写
    NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
    NSLog(@"%@",lowStr);
    //将数组中所有值全变成大写
    NSArray *uppercaseStr= [array valueForKeyPath:@"uppercaseString"];
    NSLog(@"%@",uppercaseStr);
5.3 聚合操作符

@avg:取平均值 @count:取个数 @max:取最大值 @min:取最小值 @sum:求和

- (void)aggregationOperator{
    NSMutableArray *personArray = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        LGStudent *p = [LGStudent new];
        NSDictionary* dict = @{
                               @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [p setValuesForKeysWithDictionary:dict];
        [personArray addObject:p];
    }
    NSLog(@"%@", [personArray valueForKey:@"length"]);
    
    /// 平均身高
    float avg = [[personArray valueForKeyPath:@"@avg.length"] floatValue];
    NSLog(@"%f", avg);
    
    int count = [[personArray valueForKeyPath:@"@count.length"] intValue];
    NSLog(@"%d", count);
    
    int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue];
    NSLog(@"%d", sum);
    
    int max = [[personArray valueForKeyPath:@"@max.length"] intValue];
    NSLog(@"%d", max);
    
    int min = [[personArray valueForKeyPath:@"@min.length"] intValue];
    NSLog(@"%d", min);
}

5.4 数组操作符

可以做去做相应的去重等操作

- (void)arrayOperator{
    NSMutableArray *personArray = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        GTStudent *p = [GTStudent new];
        NSDictionary* dict = @{
                               @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [p setValuesForKeysWithDictionary:dict];
        [personArray addObject:p];
    }
    NSLog(@"%@", [personArray valueForKey:@"length"]);
    // 返回操作对象指定属性的集合
    NSArray* arr1 = [personArray valueForKeyPath:@"@unionOfObjects.nick"];
    NSLog(@"arr1 = %@", arr1);
    // 返回操作对象指定属性的集合 -- 去重
    NSArray* arr2 = [personArray valueForKeyPath:@"@distinctUnionOfObjects.length"];
    NSLog(@"arr2 = %@", arr2);
    
}

5.5 嵌套操作

@distinctUnionOfArrays:去重取值 @unionOfArrays:取值

- (void)arrayNesting{
    
    NSMutableArray *personArray1 = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        GTStudent *student = [GTStudent new];
        NSDictionary* dict = @{
                            @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [student setValuesForKeysWithDictionary:dict];
        [personArray1 addObject:student];
    }
    
    NSMutableArray *personArray2 = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        GTPerson *person = [GTPerson new];
        NSDictionary* dict = @{
                            @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [person setValuesForKeysWithDictionary:dict];
        [personArray2 addObject:person];
    }
    
    // 嵌套数组
    NSArray* nestArr = @[personArray1, personArray2];
    
    NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.length"];
    NSLog(@"arr = %@", arr);
    
    NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.length"];
    NSLog(@"arr1 = %@", arr1);
}


5.6 嵌套操作另一种
- (void)setNesting{
    
    NSMutableSet *personSet1 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        GTStudent *person = [GTStudent new];
        NSDictionary* dict = @{
                            @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [person setValuesForKeysWithDictionary:dict];
        [personSet1 addObject:person];
    }
    NSLog(@"personSet1 = %@", [personSet1 valueForKey:@"length"]);
    
    NSMutableSet *personSet2 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        GTStudent *person = [GTStudent new];
        NSDictionary* dict = @{
                      @"name":@"GitArtOS",
                               @"age":@(28+i),
                               @"nick":@"GTOS",
                               @"length":@(115 + 2*arc4random_uniform(6)),
                               };
        [person setValuesForKeysWithDictionary:dict];
        [personSet2 addObject:person];
    }
    NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"length"]);

    // 嵌套set
    NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
    // 交集
    NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.length"];
    NSLog(@"arr1 = %@", arr1);
}

6. 属性验证

KVC 支持属性验证,而这一特性是通过validateValue:forKey:error: (或validateValue:forKeyPath:error:) 方法来实现的。这个验证方法的默认实现是去收到这个验证消息的对象(或keyPath中最后的对象)中根据 key 查找是否有对应的validate<Key>:error:方法实现,如果没有,验证默认成功,返回 YES。
而由于validate<Key>:error: 方法通过引用接收值和错误参数,

所以会有以下三种结果:

  • 验证成功,返回 YES,对属性值不做任何改动。
  • 验证失败,返回 NO,但对属性值不做改动,如果调用者提供了 NSError 的话,就把错误引用设置为指示错误原因的NSError对象。
  • 验证失败,返回 YES,创建一个新的,有效的属性值作为替代。在返回之前,该方法将值引用修改为指向新值对象。 进行修改时,即使值对象是可变的,该方法也总是创建一个新对象,而不是修改旧对象。
GTPerson* person = [[GTPerson alloc] init];
NSError* error;
NSString* name = @"John";
if (![person validateValue:&name forKey:@"name" error:&error]) {
    NSLog(@"%@",error);
}

那么是否系统会自动进行属性验证呢?
通常,KVC 或其默认实现均未定义任何机制来自动的执行属性验证,也就是说需要在适合你的应用的时候自己提供属性验证方法
某些其他Cocoa技术在某些情况下会自动执行验证。 例如,保存 managed object context 时,Core Data会自动执行验证。另外,在 macOS中,Cocoa Binding允许你指定验证应自动进行。

7 KVC使用的注意事项

在实际过程中, 一个类的成员变量有可能是自定义的类或者其他的复杂类型, 这时候如果想要使用KVC获取到自定义类的属性就会比较麻烦。这时KVC给我们提供了一个解决方案, 键路径 keyPath。方法如下:

- (nullable id)valueForKeyPath:(NSString *)keyPath;  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

验证代码

// 
GTStudent *student = [[GTStudent alloc] init];
student.classNumber = @"num1";
student.number = @"029";
person.student = student;
NSLog(@"classNum: %@", [personvalueForKeyPath:@"student.classNumber"]);
// 打印结果
2020-03-01 23:27:08.482381+0800 GTtest[96366:1435734] classNum: num1

上面展示keyPath的简单用法, 此时如果我们调用的方法是 valueForKey:的话, 一般情况下系统会去调用undefinedKey方法, 因为没有找到这个属性及其相关的方法和实例变量。KVC在此方法中的搜索机制首先根据" . " 来分割key, 然后在去按照上面的顺序去搜索下去。

8. KVC 异常处理

8.1 设置空值 nil处理

我们知道KVC有时候会帮我们去自动转换我们所传的值, 但是当我们传nil的时候KVC是怎么处理的呢?

- (void)setNilValueForKey:(NSString *)key;

- (void)setNilValueForKey:(NSString *)key{
    NSLog(@"GT: 设置 %@ 是空值",key);//重写这个方法做nil处理
}
@property (nonatomic, copy) NSString *subject;
@property (nonatomic, assign) int  age;
@property (nonatomic, assign) BOOL sex;
@property (nonatomic) ThreeFloats  threeFloats;

// 测试代码
  NSLog(@"******2: 设置空值******");
  [person setValue:nil forKey:@"age"]; 
  [person setValue:nil forKey:@"subject"];
  [person setValue:nil forKey:@"sex"];
  [person setValue:nil forKey:@"threeFloats"];
// 打印结果
2020-03-01 23:38:44.538117+0800 GTtest[3703:1577245] ******2: 设置空值******
2020-03-01 23:38:44:44.538200+0800 GTtest[3703:1577245] GT: 设置 age 是空值
2020-03-01 23:38:44:44.538293+0800 GTtest[3703:1577245] GT: 设置 sex 是空值
2020-02-16 16:58:44.538487+0800 GTtest[3703:1577245] GT: 设置 threeFloats 是空值

8.2 赋值时候找不到的 key

当对不存在的key进行赋值时候也会崩溃,解决办法时候就是在分类中重写下面方法即可:

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"GT: %@ 没有这key",key);
}
8.3 取值时候找不到key

对不存在的key进行取值时候也会崩溃,解决办法就是在分类中重写下面方法:

- (id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"GT: %@ 没有这key - 给你一个其他的吧,别崩溃了!",key);
    return @"NND 牛X";
}

KVC探讨-设定值 setValue: forKey:和取值valueForKey:(一)
KVC探讨-操作数组和集合、字典探讨(二)
KVC探讨-自定义KVC(四)

相关文章

  • KVC探讨-具体应用以及属性验证、异常处理(三)

    1.基本用法 直接对属性或者成员变量进行取值和赋值 2. 操作集合类型 针对集合属性,可以直接通过mutableA...

  • KVC(四)

    接上 :KVC(三 ) 接下来我们来看看 set/get异常处理、键值验证、非对象类型的处理 参考官网:Han...

  • KVC(6) 验证属性

    参考Validating Properties 默认的实现会搜索对应对象的validate:error:...

  • iOS KVC的几种情况简析

    kvc取值时,需注意的几点问题; 讲解一下kvc各种问题,包括基础属性赋值,属性对象的属性赋值,私有属性赋值 以及...

  • KVC非对象属性的表示以及属性的验证

    非对象 value的表示 NSObject提供的KVC协议方法的默认实现对对象和非对象属性都是有效的。默认的实现将...

  • 使用验证块的属性-验证块属性定义及验证规则

    使用验证块的属性 验证属性(内置验证应用程序块属性和主题中描述的属性)可以用于各种目标,包括类、字段、属性、方法和...

  • UE4材质_属性贴图0123bate1

    材质_属性 贴图 更深入探讨组成材质的属性。 通过调整这些属性,您可以选择当应用材质到物体时材质的外观,以及其如何...

  • KVC与KVO的理解与应用

    kvc简述 kvc即键值编码,在iOS中的应用主要体现在开发者通过key访问对象的属性或给对象的属性赋值。这样做最...

  • KVO和KVC的使用及原理解析

    一 KVO基本使用 二 KVO本质原理讲解及代码验证 三 KVC基本使用 四 KVC设值原理 五 KVC取值原理 ...

  • spring统一异常处理

    异常处理类 添加BaseExceptionHandler类,然后根据具体异常类型处理 入参校验及异常处理 添加依赖...

网友评论

      本文标题:KVC探讨-具体应用以及属性验证、异常处理(三)

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