专栏名称: 程序员大咖
为程序员提供最优质的博文、最精彩的讨论、最实用的开发资源;提供最新最全的编程学习资料:PHP、Objective-C、Java、Swift、C/C++函数库、.NET Framework类库、J2SE API等等。并不定期奉送各种福利。
目录
相关文章推荐
程序猿  ·  零JS!他用4.6万行HTML+CSS手搓了 ... ·  昨天  
极客之家  ·  7.3k star,一款开源高效的知识管理神器! ·  2 天前  
京东零售技术  ·  京东零售基于Flink的推荐系统智能数据体系 ... ·  2 天前  
程序猿  ·  微软重磅开源 Copilot!64 岁 ... ·  2 天前  
玉伯  ·  这个评价特别高,开心 ·  2 天前  
51好读  ›  专栏  ›  程序员大咖

KVC原理剖析

程序员大咖  · 公众号  · 程序员  · 2018-03-15 10:24

正文

请到「今天看啥」查看全文


if ([key isEqualToString:@"id"]) {

self.userId = [value integerValue];

}

}

@end


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


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


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


异常信息


当根据KVC搜索规则,没有搜索到对应的key或者keyPath,则会调用对应的异常方法。异常方法的默认实现,在异常发生时会抛出一个NSUndefinedKeyException的异常,并且应用程序Crash。


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


- (nullable id)valueForUndefinedKey:(NSString *)key;

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


异常处理


当通过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的消息通知了。


- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;


keyPath方法:


- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;

- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;


集合运算符


KVC提供的valueForKeyPath:方法非常强大,可以通过该方法对集合对象进行“深入”操作,在其keyPath中嵌套集合运算符,例如求一个数组中对象某个属性的count。(集合对象主要指NSArray和NSSet,但不包括NSDictionary)


集合运算符格式


上面表达式主要分为三部分,left部分是要操作的集合对象,如果调用KVC的对象本来就是集合对象,则left可以为空。中间部分是表达式,表达式一般以@符号开头。后面是进行运算的属性。


集合运算符主要分为三类:


  1. 集合操作符:处理集合包含的对象,并根据操作符的不同返回不同的类型,返回值以NSNumber为主。

  2. 数组操作符:根据操作符的条件,将符合条件的对象包含在数组中返回。

  3. 嵌套操作符:处理集合对象中嵌套其他集合对象的情况,返回结果也是一个集合对象。


example


下面是为了方便模拟KVC操作,而创建的测试代码。定义Transaction类为模型类,类中包含三种类型的属性。并定义BankAccount类,其中包含一个数组,下面的代码示例就都是操作这个数组的,并且数组包含所有Transaction对象。


@interface Transaction : NSObject

@property (nonatomic, strong) NSString *payee;

@property (nonatomic, strong) NSNumber *amount;

@property (nonatomic, strong) NSDate *date;

@end


@interface BankAccount : NSObject

@property (nonatomic, strong) NSArray *transactions;

@end


集合操作符


集合操作符处理NSArray和NSSet及其子类这样的集合对象,并根据不同的操作符返回不同类型的对象,返回值一般都是NSNumber。


  • @avg用来计算集合中right keyPath指定的属性的平均值。

NSNumber *transactionAverage = [self.transactions valueForKeyPath:@"@avg.amount"];







请到「今天看啥」查看全文