iOS进阶-10 KVC

在日常发开中,我们可以通过断点调试、源码查看、LLDB调试来探索技术的原理,其实文档查看也是一种极其重要的手段。
KVC(Key-Value Coding)是我们日常开发中常见的一种技术,那么底层又是如何实现的呢?我们先看官方文档Key-Value Coding Programming Guide,当然你可以在文档首页搜索其他技术的文档Apple Documentation Archive

KVC简介

Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties.
可以在文档上看到:KVC是一种能够通过NSKeyValueCoding协议直接访问对象属性的机制。

getter方法- valueForKey:

  • 1.寻找是否有这些方法get, , is, or _,如果有跳到5,没有走下一步
  • 2.判断是否是NSArray
  • 2.1寻找是否实现countOf或者objectInAtIndex: 方法,有的话就调用方法并返回值,没有就走3
  • 3.是否是NSSet
  • 3.1 寻找是否实现countOf, enumeratorOf, and memberOf:方法,有的话就调用方法并返回值,没有就走4·
  • 4.非集合类型
    • 4.1 判断accessInstanceVariablesDirectly:(是否可以直接访问成员变量:默认返回YES)方法是否返回YES
    • 4.2寻找_, _is, , or is这些成员变量
    • 4.3如果找到,直接获取实例变量的值,然后执行步骤5
  • 5.细节处理
    • 5.1如果检索到的属性值是对象指针,则只需要返回结果
    • 5.2如果该值是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回
    • 5.3如果该值是NSNumber不支持的标量类型,则转化为NSValue对象并返回
  • 6.报错 valueForUndefinedKey:

setter方法- setValue:forKey:

  • 1.简单式访问:寻找是否存在set:或者 _set的方法,如果有,那就调用,并完成;如果没有进入2
  • 2.实例变量访问:
    • 2.1 判断这个方法accessInstanceVariablesDirectly是否返回YES
    • 2.2 判断是否存在这些实例变量_, _is, , or is
    • 2.3 按顺序找到就直接赋值,并完成,不再给下面的成员变量赋值
  • 3.报错:如果1和2都有找到,那么就报错:setValue:forUndefinedKey:

KVC其他功能

1.自动转换类型

在存值时如果value的类型是NSString类的数字、结构体类型,取值时value类型会进行相应的转化成__NSCFNumber 、NSConcreteValue。eg:
Person类实现

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

@interface Person : NSObject
@property (nonatomic, copy) NSString *subject;
@property (nonatomic, assign) int  age;
@property (nonatomic, assign) BOOL sex;
@property (nonatomic) ThreeFloats  threeFloats;

@end

调用

Person *person = [[Person alloc] init];

// 1: KVC 自动转换类型
    NSLog(@"******1: KVC - int -> NSNumber - 结构体******");

    [person setValue:@18 forKey:@"age"];
    // 上面那个表达 大家应该都会! 但是下面这样操作可以?
    [person setValue:@"20" forKey:@"age"]; // int - string
    NSLog(@"%@-%@",[person valueForKey:@"age"],[[person valueForKey:@"age"] class]);//__NSCFNumber
    
    [person setValue:@"20" forKey:@"sex"];//Bool - string
    NSLog(@"%@-%@",[person valueForKey:@"sex"],[[person valueForKey:@"sex"] class]);//__NSCFNumber

    ThreeFloats floats = {1., 2., 3.};
    NSValue *value  = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];
    NSLog(@"%@-%@",[person valueForKey:@"threeFloats"],[[person valueForKey:@"threeFloats"] class]);//NSConcreteValue
设置空值
    NSLog(@"******2: 设置空值******");
    [person setValue:nil forKey:@"age"]; //age 为nil
    [person setValue:nil forKey:@"subject"];//subject不会走 - 官方注释里面说只对 NSNumber - NSValue,而NSString类型没有这个功能
找不到的 key
    NSLog(@"******3: 找不到的 key******");
    [person setValue:nil forKey:@"KC"]; 
取值时 - 找不到 key
    NSLog(@"******4: 取值时 - 找不到 key******");
    NSLog(@"%@",[person valueForKey:@"KC"]);
键值验证

KVC提供键值验证api
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

    NSLog(@"******5: 键值验证******");
    NSError *error;
    NSString *name = @"Cooci";
    if (![person validateValue:&name forKey:@"names" error:&error]) {
        NSLog(@"%@",error);
    }else{
        NSLog(@"%@",[person valueForKey:@"name"]);
    }

你可能感兴趣的:(iOS进阶-10 KVC)