iOS_KVC_分析

2018-08-31  本文已影响0人  自律_自强_通达

本文主要转载,原文链接 http://www.cocoachina.com/ios/20180305/22441.html
也参考了其他资料

一、概念定义

KVC(Key-value Coding/键值编码) :是一个非正式协议,提供了可以通过字符串间接访问类属性、方法或成员变量的一种机制。而不是通过setter 和getter 方法。(SomeBody say,KVO就是基于KVC实现的关键技术之一)

二、基础操作

KVC主要对三种类型进行操作,基础数据类型及常量对象类型集合类型

@interface BankAccount : NSObject
@property (nonatomic, strong) NSNumber *currentBalance;
@property (nonatomic, strong) Person *owner;
@property (nonatomic, strong) NSArray<transaction *> *transactions;
@end</transaction *>

在使用KVC时,直接将属性名当做key,并设置value,即可对属性进行赋值。

[myAccount setValue:@(100.0) forKey:@"currentBalance"];

三、keyPath

除了对当前对象“属性”进行赋值外,还可以对更“深层”的对象进行赋值。例如:对当前对象的address属性的 street 属性进行赋值。KVC进行多级访问时,可直接利用点语法调用即可。

[myAccount setValue:@"回龙观东大街" forKey:"adress.street"];

通过keyPath对数组进行取值的时候,并且数组的对象类型都相同,可以通过valueForKeyPath:方法指定取出数组中所有对象的某个字段。例如下面这个例子:通过valueForKeyPath:方法获取数组中所有对象的name属性值,返回放入一个数组中。

NSArray *namesArr = [array valueForKeyPath:@"name"];

四、多值操作

需要注意的是,下面两个例子虽然看到了dictionary字样,但这些并不是字典的方法。

KVC强大的地方还有,可以根据一组key获取一组 value,并以字典的形式返回,获取到字典后,就可以通过key 获取value值了。

- (NSDictionary<nsstring *, id> *)dictionaryWithValuesForKeys:(NSArray<nsstring *> *)keys;</nsstring *></nsstring *, id>

同样也可以批量赋值。在对象调用setValuesForKeysWithDictionary:方法的时候,可以传入包含key、value的字典,KVC就可以讲所有的数据按照属性名按照字典的key进行匹配,并将value赋值给对象的相应

- (void)setValuesForKeysWithDictionary:(NSDictionary<nsstring *, id> *)keyedValues;</nsstring *, id>

五、实用技巧

在开发中,最常遇到的就是将字典转模型。如果在自定义的init 方法中逐个赋值,这样每次数据发生改变的时候,还需要修改赋值语句。然而,通过KVC为我们提供的赋值API,可以对数据进行批量赋值。假设,有一下json数据,并定义User类,在外界通过setValuesForKeysWithDictionary:方法对User进行赋值。

JSON数据:
{
    "username": "lxz",
    "age": 25,
    "id": 100
}
 
@interface User : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger userId;
@end
 
@implementation User
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if ([key isEqualToString:@"id"]) {
        self.userId = [value integerValue];
    }
}
@end

赋值时会遇到一些问题,例如服务器会返回一个id字段,但是对于客户端来说id是系统保留字段,可以重写setValue:forUndefinedKey:方法并在内部处理id参数的赋值。

转换时需要服务器数据和类定义匹配,字段数量和字段名都应该匹配。如果User比服务器数据多,则服务器没传的字段为空。如果服务端传递的数据User中没有定义,则会导致崩溃。

在KVC进行属性赋值时,内部会对基础数据类型做处理,不需要手动做NSNumber的转换。需要注意的是,NSArray和NSDictionary等集合对象,value都不能是nil,否则会导致Crash。

六、异常信息

当通过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];
    }
}

集合属性操作

根据KVO的实现原理,是在运行时生成新的子类并重写其setter方法,在其内容发生改变时发送消息。但这只是对属性直接进行赋值会触发,如果属性是容器对象,对容器对象进行add或remove操作,则不会调用KVO的方法。可以通过KVC对应的API来配合使用,使容器对象内部发生改变时也能触发KVO。

在进行容器对象操作时,先调用下面方法通过key或者keyPath获取集合对象,然后再对容器对象进行add或remove等操作时,就会触发KVO的消息通知了。

(未完待续:KVC实现原理)

上一篇 下一篇

猜你喜欢

热点阅读