http://blog.csdn.net/chenglibin1988/article/details/38259865
 
Key Value Coding
Key Value Coding是cocoa的一个标准组成部分,它能让我们可以通过name(key)的方式访问property, 不必调用明确的property accssor, 如我们有个property叫做foo, 我们可以foo直接访问它,同样我们也可以用KVC来完成[Object valueForKey:@“foo”], 有同学就会问了, 这样做有什么好处呢?主要的好处就是来减少我们的代码量。
 
下面我们来看看几个例子,就明白了KVO的用法和好处了,假设这样个类叫做People,
  1. @interface People: NSObject
  2. @property (nonatomic, strong) NSString *name;
  3. @property (nonatomic, strong) NSNumber *age;
  4. @end
场景1,apple 官网的一个例子,当我们需要统计很多People的时候,每一行是一个人的实例,并且有2列属性,name, age, 这时候我们可以会这样做,
  1. - (id)tableView:(NSTableView *)tableview
  2. objectValueForTableColumn:(id)column row:(NSInteger)row {
  3. People *people = [peoleArray objectAtIndex:row];
  4. if ([[column identifier] isEqualToString:@"name"]) {
  5. return [people name];
  6. }
  7. if ([[column identifier] isEqualToString:@"age"]) {
  8. return [people age];
  9. }
  10. // And so on.
  11. }
同样我们也可以用KVC,帮助我们化简这些if, 因为name, age其实都是property, 我们可以直接通过key来访问,所以整理过后是
  1. People *people = [peopleArray objectAtIndex:row];
  2. return [people valueForKey:[column identifier]];
场景2,这下我们有了server, server的某个api(listPeople??), 会返回我们json格式一个数组,里面包含这样dict{name:xx, age:xx}这样的数据, 我们希望用这些dict数据构造出我们的people来,通常我们的做法是,为我们People类写一个static factory方法专门用来处理dict来, 把dict里面的数据取出来, 然后创建个空的People对象,然后依次设置property。然而当这样类似People的与server交互的类多了,我们就要为每个类都要加上这样的wrapper, 是否有种简单办法来设置这样的属性,当然就是我们的KVC了。
  1. -(id) initWithDictionary:(NSMutableDictionary*) jsonObject
  2. {
  3. if((self = [super init]))
  4. {
  5. [self init];
  6. [self setValuesForKeysWithDictionary:jsonObject];
  7. }
  8. return self;
  9. }
setValuesForKeysWithDictionary, 会为我们把和dictionary的key名字相同的class proerty设置上dict中key对应的value, 是不是很方便呀,但是有同学又要问了 如果json里面的某些key就是和object的property名字不一样呢,或者有些server返回的字段是objc保留字如”id”, “description”等, 我们也希望也map dict to object, 这时候我们就需要用上setValue:forUndefinedKey, 因为如果我们不处理这些Undefined Key,还是用setValuesForKeysWithDictionary就会 抛出异常。
  1. - (void)setValue:(id)value forUndefinedKey:(NSString *)key
  2. {
  3. if([key isEqualToString:@"nameXXX"])
  4. self.name = value;
  5. if([key isEqualToString:@"ageXXX"])
  6. self.age = value;
  7. else
  8. [super setValue:value forKey:key];
  9. }
所以只要重载这个方法,就可以处理了那些无法跟property相匹配的key了,默认的实现是抛出一个NSUndefinedKeyException,又有同学发问了如果 这时候server返回的People有了内嵌的json(如Products{product1{count:xx, sumPrice:xx}}, product2{} ….),又该怎么办,能把这个内嵌的json转化成我们的客户端的Product类嘛, 当然可以这时候就需要重载setValue:forKey, 单独处理”Products”这个key, 把它wrapper成我们需要的class
  1. -(void) setValue:(id)value forKey:(NSString *)key
  2. {
  3. if([key isEqualToString:@"products"])
  4. {
  5. for(NSMutableDictionary *productDict in value)
  6. {
  7. Prodcut *product = [[Product alloc] initWithDictionary:prodcutDict];
  8. [self.products addObject:product];
  9. }
  10. }
  11. }
场景3,我们需要把一个数组里的People的名字的首字母大写,并且把新的名字存入新的数组, 这时候通常做法会是遍历整个数组,然后把每个People的name取出来,调用 capitalizedString 然后把新的String加入新的数组中。 有了KVC就有了新做法:
 
  1. [array valueForKeyPath:@"name.capitalizedString"]
 
我们看到valueForKeyPath, 为什么用valueForKeyPath, 不用valueForKey, 因为valueForKeyPath可以传递关系,例如这里是每个People的name property的String的capitalizedString property, 而valueForKey不能传递这样的关系,所以对于dict里面的dict, 我们也只能用valueForKeyPath。这里我们也看到KVC对于array(set), 做了特殊处理,不是简单操作collection上,而是 针对这些collection里面的元素进行操作,同样KVC也提供更多地操作,例如@sum这些针对collection,有兴趣的同学可以去用下。
 
场景4,当我们执行NSArray *products = [people valueForKey:@“products”],我们希望的是[people products],可是people没有这样的方法, KVC又会为我们带来些什么呢?
 
首先会去找getProdcuts or products or isProducts, 按照这样的顺序去查找,第一个找到的就返回
然后会去找countOfProducts and either objectInProductsAtIndex: or ProductsAtIndexes, 如果找到,就会去找countOfProducts and enumeratorOfProducts and memberOfProducts 这个2个方法都找到了,KVC才会给我们返回一个代理的NSKeyValueArray,用于我们后续的操作(addProduct之类的)。
 
如果有个变量叫做 products, isProducts, products or isProducts, KVC会直接就使用这样的变量,如果你觉得直接用这样的变量是破坏了封装, 可以禁止这样的行为发生,重载 +accessInstanceVariablesDirectly,返回NO。
 
简单来说,valueForKey, 会给我们带来一个代理array, 如果我们实现了某些方法,上诉的这些方法只是针对NSArray, 对于mutable的collection, 我们还需要提供其他 方法的实现才行。
 
Key Value Observing
Key Value Observing, 顾名思义就是一种observer 模式用于监听property的变化,KVO跟NSNotification有很多相似的地方, 用addObserver:forKeyPath:options:context:去start observer, 用removeObserver:forKeyPath:context去stop observer, 回调就是observeValueForKeyPath:ofObject:change:context:。
 
  1. - (void)removeObservation {
  2. [self.object removeObserver:self
  3. forKeyPath:self.property];
  4. }
  5. - (void)addObservation {
  6. [self.object addObserver:self forKeyPath:self.property
  7. options:0
  8. context:(__bridge void*)self];
  9. }
  10. - (void)observeValueForKeyPath:(NSString *)keyPath
  11. ofObject:(id)object
  12. change:(NSDictionary *)change
  13. context:(void *)context {
  14. if ((__bridge id)context == self) {
  15. // 只处理跟我们当前class的property更新
  16. }
  17. else {
  18. [super observeValueForKeyPath:keyPath ofObject:object
  19. change:change context:context];
  20. }
  21. }
 
对于KVO来说,我们要做的只是简单update 我们的property数据,不需要像NSNotificationCenter那样关心是否有人在监听你的请求,如果没有人监听该怎么办, 所有addObserver, removeObserver, callback 都是想要监听的你的property的class做的事情。 曾经做个项目,用NSNotificationCenter post Notification在一个network callback里面,可是这时候因为最早的addObserver的class被释放了, 接着生成的addObserver的class, 就接受到了上一个observer该监听的事件,所以造成了错误,那时候的解决方案是为addObserve key做unique,不会2次addObserver 的key是相同的,但是有了KVO, 我们同样可以用KVO来完成,当addOberver的的object remove的时候,就不会有这样的callback被调用了。
 
KVO给我们提供了更少的代码,和比NSNotification好处,不需要修改被观察的class, 永远都是观察你的人做事情。 但是KVO也有些毛病, 1. 如果没有observer监听key path, removeObsever:forKeyPath:context: 这个key path, 就会crash, 不像NSNotificationCenter removeObserver。 2. 对代码你很难发现谁监听你的property的改动,查找起来比较麻烦。 3. 对于一个复杂和相关性很高的class,最好还是不要用KVO, 就用delegate 或者 notification的方式比较简洁。
 
Summary
尽量使用KVC可以大大地减少我们的代码量,当遇到property的时候,可以多想想是否可以KVC来帮助我,是否可以用KVC来重构代码, 当需要加入observer模式时,可以考虑下KVO, 在高性能的observer里面,KVO会给我们很好的帮助。
 

【iOS】KVC 和 KVO 的使用场景的更多相关文章

  1. iOS:KVC和KVO

    来源:  对月流 链接:http://www.jianshu.com/p/f1393d10109d 写在前面: 关于KVC和KVO各种博客多了去了,重新整理下,就当是温习一下吧,也还算是个新手,不对的 ...

  2. iOS KVC 和 KVO 的学习

    KVC  (NSKey Value Coding) :键值编码 KVO (Key Value Observing) :键值监听 前言:我曾经用过 监听 一个音频何时结束 监听视频播放 状态等 用了这种 ...

  3. [iOS] KVC 和 KVO

    开发iOS经常会看见KVO和KVC这两个概念,特地了解了一下. 我的新博客wossoneri.com link KVC Key Value Coding KVC是一种用间接方式访问类的属性的机制.比如 ...

  4. iOS KVC 和 KVO 区别简单总结

    KVC: key value coding,键值编码.是一种通过使用属性的名称(key)来间接访问对象属性的方法.这个方法可以不用通过 setter/getter 方法来访问对象的属性.该方法使用的实 ...

  5. 【原】iOS中KVC和KVO的区别

    在iOS开发中经常会看到KVC和KVO这两个概念,比较可能混淆,特地区分一下 KVC(Key Value Coding) 1> 概述 KVC:Key Value Coding,键值编码,是一种间 ...

  6. iOS中关于KVC与KVO知识点

    iOS中关于KVC与KVO知识点 iOS中关于KVC与KVO知识点  一.简介 KVC/KVO是观察者模式的一种实现,在Cocoa中是以被万物之源NSObject类实现的NSKeyValueCodin ...

  7. iOS开发中KVC、KVO简介

    在iOS开发中,KVC和KVO是经常被用到的.可以使用KVC对对象的属性赋值和取得对象的属性值,可以使用KVO监听对象属性值的变化.简单介绍一下KVC和KVO. 一:键值编码(KVC) KVC,全称 ...

  8. iOS开发-KVC和KVO的理解

    KVC和KVO看起来很专业,其实用起来还是比较简单的,KVC(Key-value coding)可以理解为键值对编码,如果对象的基本类型,那么键值对编码实际上和get,set方法没有区别,如果是属性是 ...

  9. iOS编程——Objective-C KVO/KVC机制[转]

    这两天在看和这个相关的的内容,全部推翻重写一个版本,这是公司内做技术分享的文档总结,对结构.条理做了更清晰的调整.先找了段代码,理解下,网上看到最多的一段的关于KVC的代码 先上代码 1.     1 ...

随机推荐

  1. bzoj 4457: 游戏任务

    4457: 游戏任务 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 128  Solved: 71[Submit][Status][Discuss] D ...

  2. Ubuntu使用iptables配置防火墙提示:unrecognized service(Ubuntu配置iptables防火墙)

    Ubuntu默认安装是没有开启任何防火墙的. 当使用service iptables status时发现提示iptables:unrecoginzed service.意思是无法识别的服务. 以下方法 ...

  3. java内部类理解使用

    这是我学习Java内部类的笔记 1.为什么使用内部类?使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响1.1 ...

  4. 【c++】面向对象程序设计之关于继承

    面向对象程序设计的核心思想是数据抽象(类的接口与实现分离).继承和动态绑定 基类 虚函数:基类希望派生类各自定义适合自身的版本的函数 在c++中,当我们使用基类的引用或指针调用虚函数时将发生动态绑定. ...

  5. 数据库系统学习(七)-SQL语言之复杂查询与视图

    第七讲 SQL语言之复杂查询与视图 基本内容 子查询 IN与NOT IN谓词子查询 判断某一表达式的值是否在子查询的结构中 非相关子查询 相关子查询 theta some /theta all谓词子查 ...

  6. WMS8_仓库配置

    仓库划分为不同的区域,比如收货区,出货区,库存区,这些区域在odoo中称之为库位[location],库位可以上下嵌套,形成一个树状的层级关系. 库位分为不同的类型 物理库存位置[Physical s ...

  7. ftk学习记(combox篇)

    [声明:版权全部,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 上一篇文章谈到了多窗体,还是依照约定看一下效果是什么样的. 假设大家细心一点.就会发现窗体中的l ...

  8. Linux CentOS下安装、配置mysql数据库

    假设要在Linux上做j2ee开发.首先得搭建好j2ee的开发环境.包含了jdk.tomcat.eclipse的安装(这个在之前的一篇随笔中已经有具体解说了Linux学习之CentOS(七)--Cen ...

  9. Python - scrapy安装中libxml2问题

    先到 http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 下载下面三个库的 whl,然后用pip install 来安装即可 pycurl,lxml,lib ...

  10. eclipse无法启动问题记录

    几天没开eclipse,居然报错“can not unload……”,搜索答案发现没有准确的,遵从了一个多人顶赞的办法重下eclipse,把配置文件拷贝一份,结果悲剧了,虽然能够打开了,但我之前配置的 ...