【iOS-KVC学习】

KVC

KVC是什么

  • KVC是Key-Value-Coding的缩写,通过查询,俗称键值编码。也可以说在iOS开发的过程KVC提供的机制允许我们通过key值访问对象的属性或成员变量,
  • 和KVO一样KVC也是针对NSOBject子类的一种方法,其中在NSKeyValueCoding中提供了KVC通用的访问方法,分别是getter方法valueForKey和setter方法setValue:forKey,以及其衍生的keyPath方法,这两个方法是各个类通用的。并且由KVC提供默认的实现,我们也可以自己重写对应的方法来改变实现。

KVC的基础方法API

  • KVC的定义都是对NSObject的扩展来实现的,KVC较为重要的四个方法如下
- (nullable id)valueForKey:(NSString *)key;      //直接通过Key来取值

- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值

- (nullable id)valueForKeyPath:(NSString *)keyPath;   //通过KeyPath来取值

- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
  • 四个方法可以分为俩类总结
  • 设值
    • - (void)setValue:(nullable id)value forKey:(NSString *)key;
    • -(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
  • 取值:
    • (nullable id)valueForKey:(NSString *)key;
    • - (nullable id)valueForKeyPath:(NSString *)keyPath;

KVC设值

key方法
  • 在使用的时候我们将属性名写在Key方法,Value处写我们的属性赋值
Person* person = [[Person alloc]init];
 [person setValue:@101 forKey:@"pAge"];
keyPath方法
  • 多级访问-KeyPath路径方法,KVC进行多级访问时,类似于属性调用一样用点语法进行访问即可
    请添加图片描述
  • 设置两个继承于 NSObject的类- Person Dog,在Person里设置一个Dog属性,Dog类有一个age属性,我们通过KVC的keyPath路径方法来设置Dog类的age;
  • person
#import <Foundation/Foundation.h>
#import "Dog.h"
NS_ASSUME_NONNULL_BEGIN
/***
 KVC - 成员变量
 
 */
@interface Person : NSObject {
}
/**
 添加Dog属性
 */
@property (nonatomic, strong)Dog* dog;
@end
  • Dog
@interface Dog : NSObject
@property (nonatomic, assign)int age;
@end
  • 在ViewController里打印试试
 Person* person = [[Person alloc]init];
 [person setValue:@120 forKeyPath:@"dog.age"];
    /***
     KVC- 嵌套适用,可以用KeyPath的点语法
     */
     //这里牵扯到了获取值方法,也是多级访问
 id age2 = [person valueForKeyPath:@"dog.age"];![请添加图片描述](https://img-blog.csdnimg.cn/5fc133845dd14bfda5db043b0e3b3e35.png)

 NSLog(@"keyPath获得的年龄是%@", age2);

请添加图片描述

KVC设值的异常

  • 在设值的过程里一旦不小心把值置为nil,我们需要重写setNilValueForKey: 即可

KVC取值

  • 和上面设值一样,取值也是2个方法valueForKey,valueForKeyPath,这个和设置一样,多重设值就是keyPath的点语法,这里写一个demo探究一下取值过程的先后顺序,经过测试,取值过程有2个优先级
  • 这里仅用一个Person类来探究取值过程的先后顺序
  • 剽窃的图
    在这里插入图片描述
第二优先级
  • Person类里设置一个name的成员变量,而编译器在寻找value的时候存在四个不同的命名,_key, _isKey, key, isKey
#import <Foundation/Foundation.h>
#import "Dog.h"
NS_ASSUME_NONNULL_BEGIN
/***
 KVC - 成员变量
 
 */
@interface Person : NSObject {
    NSString* _name;
    NSString* _isName;
    NSString* name;
    NSString* isName;
}
  • 在调用valueForKey方法的时候,我们重写init方法
#import "Person.h"
#import "Dog.h"
@implementation Person
- (instancetype)init {
    if (self = [super init]) {
        //成员变量
        _name = @"_name";
        _isName = @"_isName";
        name = @"name";
        isName = @"isName";
    }
    return self;
}

  • 打印
 Person* person = [[Person alloc]init];
    NSString* name = [person valueForKey:@"name"];
    NSLog(@"%@", name);

请添加图片描述

  • 当这四种成员变量都存在的时候编译器先去寻找_key变量,没有_key呢?
    请添加图片描述
  • 经过测试,在进行编译的时候按照_key,_isKey,key,iskey的顺序查找,
+ (BOOL)accessInstanceVariablesDirectly
  • 其实编译器能否取查找_key,_isKey,key,iskey在这之前取决于一个函数
  • +(BOOL)accessInstanceVariablesDirectly,
    默认返回YES,表示如果没有找到Set方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索,如果返回NO系统会抛出异常
  • 测试,在没有Setter方法的前提下
+ (BOOL)accessInstanceVariablesDirectly {
    return NO; 
}

请添加图片描述

  • 抛出异常,我们需要重写图中的- (id)valueForUndefinedKey:(NSString *)key
    返回nil即可解决异常
- (id)valueForUndefinedKey:(NSString *)key {
    return nil;
}
  • 测试请添加图片描述
第一优先级Setter方法
  • 前面提到的都是系统在么有找到Setter方法的前提在+ (BOOL)accessInstanceVariablesDirectly 返回YES去寻找key,但最高的优先级是setter方法,编译器在上面的函数返回YES之前寻找setter方法
  • person
  • 把init和setter方法全部打开,然后设置打印不同的名字,看出现什么,即可代表了优先级的顺序
#import <Foundation/Foundation.h>
#import "Dog.h"
NS_ASSUME_NONNULL_BEGIN
/***
 KVC - 成员变量
 
 */
@interface Person : NSObject {
    NSString* _name;
    NSString* _isName;
    NSString* name;
    NSString* isName;
    int pAge;
}


#import "Person.h"
#import "Dog.h"
@implementation Person
- (instancetype)init {
    if (self = [super init]) {
        //成员变量
        _name = @"_name";
        _isName = @"_isName";
        name = @"name";
        isName = @"isName";
    }
    return self;
}

/***
 第一优先级,3个Setter方法,getKey > key >isKey
 */
- (NSString*)name {
    return @"Hank";
}
- (NSString*)getName {
    return @"getHank";
}
- (NSString*)isName {
    return @"isHank";
}

请添加图片描述

  • 通过代码可知在setter方法都存在的时候也是有优先级的,getKey > key > isKey
  • 如果setter方法都不存在那么系统会调用(BOOL)accessInstanceVariablesDirectly 的返回值按照之前的顺序查找value

第一优先级的有序集合类方法

尝试返回一个Int
  • 把getName里返回成int 试试发现
- (int)getName {
    /***
     发现第二优先级方法的来源
     name被包装成了NSDCFNumeber类型
     */
    return 10;
}
  • 打断点发现,当我们把返回值返回一个int类的时候系统会把 name被包装成了NSDCFNumeber类型,如此引出了第一优先级之后的两个方法
  • -(NSInteger)countOfName
  • -(id)objectInNameAtIndex :(NSInteger)index
    请添加图片描述
  • 发现来源- 在3个getter方法里返回一个 int 发现系统包装成了
    第二优先级,当3个getter方法不存在的时候,系统调用这个方法,生成一个NSKeyValueArray数组!
  • 测试 Person.m,把第一优先级的方法注释,对比init和上面的方法
#import "Person.h"
#import "Dog.h"
@implementation Person
- (instancetype)init {
    if (self = [super init]) {
            //成员变量
        _name = @"_name";
        _isName = @"_isName";
        name = @"name";
        isName = @"isName";
    }
    return self;
}

/***
 第一优先级,3个Setter方法,getKey > key >isKey
 */
//- (NSString*)name {
//    return @"Hank";
//}
//- (NSString*)getName {
//    return @"getHank";
//}
- (int)getName {
    /***
     发现第二优先级方法的来源
     name被包装成了NSDCFNumeber类型
     */
    return 10;
}
//- (NSString*)isName {
//    return @"isHank";
//}

/***
 发现来源- 在3个getter方法里返回一个 int 发现系统包装成了
 第二优先级,当3个getter方法不存在的时候,系统调用这个方法,生成一个NSKeyValueArray数组!新发现
 */
- (NSInteger)countOfName {
    return 12;
}
- (id)objectInNameAtIndex :(NSInteger)index{
    return @"objectName";
}

请添加图片描述

  • 可以看出俩个函数,一个是返回长度,一个是返回内容的,当setter方法不存在的时候会先调用这个函数,(了解即)

取值优先级的总结

  • 第一优先级,先找相关方法-,getKey > key > isKey
  • 上述方法不存在,找- -(NSInteger)countOfName And
    -(id)objectInNameAtIndex :(NSInteger)index
    -当上述第一优先级都不存在的时候,系统查找(BOOL)accessInstanceVariablesDirectly返回值,YES则按_key,_isKey,key,iskey顺序寻找变量取值,NO则会抛出异常,需要重写- (id)valueForUndefinedKey:(NSString *)key 方法

KVC批量动态取值方法(2种)

  • KVC还可以根据给定的一组key,获取到一组value,并且以字典的形式返回,获取到字典后可以通过key从字典中获取到value
    -NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
  • 也可以通过KVC进行批量赋值。在对象调用setValuesForKeysWithDictionary:方法时,可以传入一个包含key、value的字典进去,KVC可以将所有数据按照属性名和字典的key进行匹配,并将value给User对象的属性赋值。
    - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
  • 新建newPerson
@interface newPerson : NSObject
@property (nonatomic, assign) NSInteger pAge;
@property (nonatomic, copy) NSString* pName;
@property (nonatomic, copy) NSString* pSex;
@end
  • ViewController
在这里插入代码片#import "Person.h"
#import "ViewController.h"
#import "newPerson.h"
/**
 
 
 
 
 
 */
@interface ViewController () 

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    /**
     KVC批量取值测试
     */
    NSDictionary* pDictionary = @{@"pName":@"Lyt", @"pAge":@"19", @"pSex":@"Girl"};
    newPerson* Nperson = [[newPerson alloc] init];
    [Nperson setValuesForKeysWithDictionary:pDictionary];
    NSLog(@"newPerson.pName: %@", Nperson.pName);
    NSLog(@"newPerson.pAge: %ld", Nperson.pAge);
    NSLog(@"newPerson.pSex: %@", Nperson.pSex);
    
    NSDictionary* returnDictionary = [Nperson dictionaryWithValuesForKeys:@[@"pName", @"pAge", @"pSex"]];
    NSLog(@"returnDictionary%@", returnDictionary);
}
@end

请添加图片描述

类的属性和字典不匹配

  • 对于传入的字典里KVC还可以根据给定的一组key,获取到一组value,并且以字典的形式返回,获取到字典后可以通过key从字典中获取到value的方法,如果字典里出现了类没有的属性,系统会崩溃,我把pSex改成了Sex
NSDictionary* pDictionary = @{@"pName":@"Lyt", @"pAge":@"19", @"Sex":@"Girl"};
    newPerson* Nperson = [[newPerson alloc] init];
    [Nperson setValuesForKeysWithDictionary:pDictionary];
    NSLog(@"newPerson.pName: %@", Nperson.pName);
    NSLog(@"newPerson.pAge: %ld", Nperson.pAge);
    NSLog(@"newPerson.pSex: %@", Nperson.pSex);

请添加图片描述

重写-(void)setValue:(id)value forUndefinedKey:(NSString *)key
  • 如果类和字典不匹配(字典的传入数量大于类),在不匹配的类的M文件里面重写-(void)setValue:(id)value forUndefinedKey:(NSString *)key方法即可
#import "newPerson.h"

@implementation newPerson
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if ([key isEqualToString:@"Sex"]) {
        self.pSex = (NSString*) value;
    }
}
@end

总结

  • KVO和KVC还有联系,日后会更新KVO和KVC的联系
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值