KVC用法总结

KVC全称是Key Value Coding,定义在NSKeyValueCoding.h文件中,相当是一个代理,是对NSObject的扩展来实现的。

KVC提供了一种间接访问其属性方法成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。

  

作用:1.在运行时动态在访问和修改对象的属性,而不是在编译时确定.

         2.继承了NSObject类的,都能使用KVC


 

常用api:

- (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来设值

一.KVC对数值和结构体型属性的支持:

@interface Person : NSObject
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,assign) int ID;
@end
        Person *p = [[Person alloc] init];

        [p setValue:10086 forKey:@"ID"]; 报错
key值为int类型的value必须要进行NSNumber转换[NSNumber numberWithInt:i] 否者会报标题上得错误

 


        [p setValue:[NSNumber numberWithInteger:5] forKey:@"age"];
        NSLog(@"%ld,%d",p.age,p.ID);


       [p valueForKey:@"age"]; 会以NSNumber的形式返回age的值。


1.我们不能直接将一个数值通过KVC赋值的,我们需要把数据转为NSNumber或NSValue类型传入。

2.通过key,返回的值是NSNumber类型的.

可以使用NSNumber的数据类型有:基础的数据类型

可以使用NSValue的数据类型有:结构体型的数据,CGPoint、CGSize,任何结构体都是可以转化成NSValue对象的,包括其它自定义的结构体。


二.KVC中使用KeyPath:

1.除了对当前对象的属性进行赋值外,还可以对其更“深层”的对象进行赋值,Person类中,有book类的属性,book类中有Backname.

2.数组进行取值时,并且数组中存储的对象类型都相同,可以通过valueForKeyPath:方法指定取出数组中所有对象的某个字段。

@class Book;
@interface Person : NSObject
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,assign) int ID;
@property (nonatomic,strong) Book books;
@end

@interface Book : NSObject
@property (nonatomic,copy) NSString name;
@end
Person *p = [Person new];
Book *b = [Book new];
p.books = b;
 book必须实例化,不然找不到book里面属性赋值
        [p setValue:@"小白" forKeyPath:@"book.name"];
        
        NSLog(@"%@",p.books.name);

 NSArray *arry = @[p];
        [arry setValue:@(18) forKeyPath:@"age"];
        NSLog(@"%@",[arry valueForKeyPath:@"age"]);

三.字典转模型:

当对NSDictionary对象使用KVC时,valueForKey:的表现行为和objectForKey:一样。

所以使用valueForKeyPath:用来访问多层嵌套的字典是比较方便的。

两种常用api:

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

dictionaryWithValuesForKeys:是指输入一组key,返回这组key对应的属性,再组成一个字典。
setValuesForKeysWithDictionary:是用来修改Model中属性名与key名相同的属性。

@interface Address : NSObject
 
@end
 
@interface Address()
 
@property (nonatomic,copy)NSString* country;
@property (nonatomic,copy)NSString* province;
@property (nonatomic,copy)NSString* city;
@property (nonatomic,copy)NSString* district;
 
@end
 
@implementation Address
 
@end
 
 
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //模型转字典
        Address* add = [Address new];
        add.country = @"China";
        add.province = @"Guang Dong";
        add.city = @"Shen Zhen";
        add.district = @"Nan Shan";
        NSArray* arr = @[@"country",@"province",@"city",@"district"];
        NSDictionary* dict = [add dictionaryWithValuesForKeys:arr]; //把对应key所有的属性全部取出来
        NSLog(@"%@",dict);
        
        //字典转模型
        NSDictionary* modifyDict = @{@"country":@"USA",@"province":@"california",@"city":@"Los angle"};
        [add setValuesForKeysWithDictionary:modifyDict];            //用key Value来修改Model的属性
        NSLog(@"country:%@  province:%@ city:%@",add.country,add.province,add.city);
 
        
    }
    return 0;
}
打印结果:

{
    city = "Shen Zhen";
    country = China;
    district = "Nan Shan";
    province = "Guang Dong";
}

country:USA  province:california city:Los angle

四.异常信息 :

当根据KVC搜索规则,没有搜索到对应的key或者keyPath,则会调用对应的异常方法。

异常方法的默认实现,在异常发生时会抛出一个NSUndefinedKeyException的异常,并且应用程序Crash

我们可以重写下面两个方法,根据业务需求合理的处理KVC导致的异常:

- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Animal 0x100743120> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key height.'
*** First throw call stack:

 五.异常处理:

当通过KVC给某个非对象的属性赋值为nil时,此时KVC会调用属性所属对象的setNilValueForKey:方法,

并抛出NSInvalidArgumentException的异常,并使应用程序Crash

我们可以通过重写下面方法,在发生这种异常时进行处理。例如给name赋值为nil的时候,就可以重写setNilValueForKey:方法并表示name是空的。

- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"name"]) {
        [self setValue:@"" forKey:@”age”];
    } else {
        [super setNilValueForKey:key];
    }
}

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<Animal 0x10042f600> setNilValueForKey]: could not set nil as the value for the key age.'

六.探索规则:

KVC在通过key或者keyPath进行操作的时候,可以查找属性方法、成员变量等,查找的时候可以兼容多种命名。具体的查找规则要以官方文档为主.

  1. 在学习KVC的搜索规则前,要先弄明白一个属性的作用,这个属性在搜索过程中起到很重要的作用。
  2. 这个属性表示是否允许读取实例变量的值,如果为YES则在KVC查找的过程中,从内存中读取属性实例变量的值。默认YES。
  3. @property (class, readonly) BOOL accessInstanceVariablesDirectly;
    

     

1.setValue:forKey:方法赋值的原理 :

[item setValue:@”value” forKey:@”property”]


流程:
1. 首先去类中查找有没有setProperty方法,找到,直接调用赋值 [self setProperty:@”value”]方法.

2. 去类中查找有没有property属性,有,直接访问属性赋值  property = value

3. 去类中查找有没有_property成员变量,有,直接访问属性赋值 _property = value

4. 找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误



执行上面流程的前提accessInstanceVariablesDirectly是yes

如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUNdefinedKey:方法。

注意:1.属性的名字必须和键相同,否则找不到相关属性会报错

            2.必须保证类中定义的属性要大于或等于字典中key的数量。

 

2.valueForKey:

[item valueForKey:@"property"];


流程:

1.首先查找 类中 属性的get方法,找到的话会直接调用。如果是BOOL或者Int等值类型, 会将其包装成一个NSNumber对象。

2.如果get方法没有找到,KVC则会查找KVC类中countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes格式的方法。
如果countOf<Key>方法和另外两个方法中的一个被找到,那么就会返回一个可以响应NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子类),调用这个代理集合的方法,或者说给这个代理集合发送属于NSArray的方法,就会以countOf<Key>,objectIn<Key>AtIndex 或<Key>AtIndexes这几个方法组合的形式调用。
还有一个可选的get<Key>:range:方法。所以你想重新定义KVC的一些功能,你可以添加这些方法,需要注意的是你的方法名要符合KVC的标准命名方法,包括方法签名。

3.如果第二步奏的方法没有找到,那么会同时查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet所的方法的代理集合,和上面一样,给这个代理集合发NSSet的消息,就会以countOf<Key>,enumeratorOf<Key>,memberOf<Key>组合的形式调用。


4.如果还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),那么和先前的设值一样,会按_<key>,_is<Key>,<key>,is<Key>的顺序搜索成员变量名,这里不推荐这么做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了类方法+ (BOOL)accessInstanceVariablesDirectly返回NO的话,那么会直接调用valueForUndefinedKey:

5.还没有找到的话,调用valueForUndefinedKey

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值