整理了一下关于KVO的姿势
http://www.jianshu.com/p/d104daf7a062
1)
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
是为了防止系统重复实现;
2)willChangeValueForKey:
和didChangeValueForKey:
调用时都会调用valueForKey:
,并把得到的结果分别当成old value和new value,以告知观察者。
3)valueForKey只在观察者关心时调用。
其实是第一次看Key-Value Observing Programming Guide和Key-Value Coding Programming Guide。虽然以前也写过一点KVO的代码,但是当时为了快点赶晚项目,随便看了看示例代码就贴到工程里了。是一个不好的习惯呢。所以还是把一些细节弄弄清楚,把姿势整理一下吧^ ^
设立一个观察者
KVO(Key-value observing)提供了一种机制,当某个对象的某个特定的property被改动时,它能够允许别的对象接到这个通知。
实现KVO的前提条件就是,这个类和它的这个property是KVC compliant的。对于这个类来说,valueForKey:
和setValue:forKey:
这两个方法被合理的实现(NSObject对这两个方法有了默认的实现);如果这个property是一个attribute或者to-one relationship的话,要满足以下几个条件的至少一个(其中,key表示这个property对应的key):
- 这个类以key为名,声明了一个property;
- 这个类实现了以key为名的accessor方法;
- 这个类以key或者_key为名,声明了一个实例变量。
如果这个property是一个to-many relationship,情况会更复杂,还是把官方文档搬出来吧。
对一个property设定一个观察者需要这样几个步骤:
一. 使用addObserver:forKeyPath:options:context:
方法,对需要观察的property注册一个观察者
这时,观察者对象和被观察的对象之间会建立联系,这种联系是针对于对象的,而不是针对于类的。
其中可以指定options参数:
NSKeyValueObservingOptionNew
:当options中包括了这个参数的时候,观察者收到的change参数中就会包含NSKeyValueChangeNewKey
和它对应的值,也就是说,观察者可以得知这个property在被改变之后的新值。NSKeyValueObservingOptionOld
:和NSKeyValueObservingOptionNew
的意思类似,当包含了这个参数的时候,观察者收到的change参数中就会包含NSKeyValueChangeOldKey
和它对应的值。NSKeyValueObservingOptionInitial
:当包含这个参数的时候,在addObserver的这个过程中,就会有一个notification被发送到观察者那里,反之则没有。NSKeyValueObservingOptionPrior
:当包含这个参数的时候,在被观察的property的值改变前和改变后,系统各会给观察者发送一个change notification;在property的值改变之前发送的change notification中,change参数会包含NSKeyValueChangeNotificationIsPriorKey
并且值为@YES
,但不会包含NSKeyValueChangeNewKey
和它对应的值。
可以指定多个NSKeyValueObservingOptions
,将他们用“或”连接后,作为options参数。
可以将任意对象作为context参数,它会和观察者实现的observeValueForKeyPath:ofObject:change:context:
方法中的context参数指向同一个对象。
二. 观察者必须实现observeValueForKeyPath:ofObject:change:context:
方法,并定义观察者应该如何响应change notification
在这个方法中,change参数会传入一个NSDictionary,代表了与property的值变化相关的信息。其中可能会有这样几个键值对:
NSKeyValueChangeKindKey
:这是change中永远会包含的键值对,它的值时一个NSNumber对象,具体的数值有NSKeyValueChangeSetting
、NSKeyValueChangeInsertion
、NSKeyValueChangeRemoval
、NSKeyValueChangeReplacement
这几个,其中后三个是针对于to-many relationship的。NSKeyValueChangeNewKey
:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionNew
,这个键值对才会被change参数包含;它表示这个property改变后的新值。NSKeyValueChangeNewOld
:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionOld
,这个键值对才会被change参数包含;它表示这个property改变前的值。NSKeyValueChangeIndexesKey
:当被观察的property是一个ordered to-many relationship时,这个键值对才会被change参数包含;它的值是一个NSIndexSet对象。NSKeyValueChangeNotificationIsPriorKey
:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionPrior
,这个键值对才会被change参数包含;它的值是@YES
。
三. 当被观察的property的值发生变化的时候,或者它依赖的某一个key的值发生变化的时候,observeValueForKeyPath:ofObject:change:context:
方法会自动被调用
这里的改变被观察的property的值,指的应该是这样几种方式中的一种:
- 调用key-value compliant的accessor方法;
- 使用key-value coding方法,如
setValue:forKey:
、insertValue:inPropertyWithKey:
; - 使用
mutableArrayValueForKey:
取得一个代理对象,并操作这个代理对象。
所以如果仅仅是改变了某个property所生成的instance variable的值,自动的change notification是不会发送的。
四. 当不再需要这个观察者的时候,需要调用removeObserver:forKeyPath:
或者removeObserver:forKeyPath:context:
方法,移除这个观察者
应该尽量使用removeObserver:forKeyPath:context:
方法,因为如果当同样的observer和同样的key path被多次注册,但是每次注册使用的是不同的context,这时如果使用removeObserver:forKeyPath:
方法,它就需要猜测到底移除哪一个观察者,当然这很可能猜错。
那些在addObserver:forKeyPath:options:context:
中指定的对象,必须在deallocate之前被移除掉。
手动发送change notification
按照上面说的方法,为某个property设立一个观察者,这个观察者就会收到系统自动发送的change notification。然而,第三方程序员也可以手动发送change notification。手动发送change notification可以更自由的控制通知发送的逻辑。
如果一个类想要实现手动的change notification发送,则必须重写NSObject实现的automaticallyNotifiesObserversForKey:
方法,并对需要实现手动发送的key返回NO
,其余则调用super。
官方示例代码:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"openingBalance"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
然后在property的值改变之前调用willChangeValueForKey:
,在值改变之后调用didChangeValueForKey:
。当然,在什么样的情况下才调用这两个方法,是由第三方程序的逻辑决定的。
如果一个操作造成了多个key的值的改变,则willChangeValueForKey:
和didChangeValueForKey:
必须嵌套着调用。
官方示例代码:
- (void)setOpeningBalance:(double)theBalance {
[self willChangeValueForKey:@"openingBalance"];
[self willChangeValueForKey:@"itemChanged"];
_openingBalance = theBalance;
_itemChanged = _itemChanged+1;
[self didChangeValueForKey:@"itemChanged"];
[self didChangeValueForKey:@"openingBalance"];
}
willChangeValueForKey:
和didChangeValueForKey:
调用时都会调用valueForKey:
,并把得到的结果分别当成old value和new value,以告知观察者。
做了个小实验,发现在自动发送change notification的情况下,willChangeValueForKey:
和didChangeValueForKey:
也会被调用,看来系统也是通过这两个方法来发送通知的。
如果手动发送change notification的property是ordered to-many relationship,则不仅要指定被改变的key,还要指定改变的类型和index。类型用NSKeyValueChange
来表示。
注册dependent keys
对于to-one relationship的property,重写keyPathsForValuesAffectingValueForKey:
或者keyPathsForValuesAffecting<Key>
方法可以定义这个key所依赖的一系列key。但是这个方法仅限于to-one relationship的property。
官方示例代码:
+ (NSSet *)keyPathsForValuesAffectingFullName {
return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}
而对于to-many relationship,要么手动观察每一个依赖的key,要么利用Core Data来完成这个任务。
整理了一下关于KVO的姿势的更多相关文章
- Java迭代 : Iterator和Iterable接口
从英文意思去理解 Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的.able结尾的表示 能...样,可以做.... Iterator: 在英语中or 结尾是都是表示 .. ...
- java学习--Iterable 和 Iterator
Iterable Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的. 一个集合对象要表明自己支持迭代,能有使用foreach语句的特权,就必须实现Iterable接口,表明我 ...
- 【转】Java迭代:Iterator和Iterable接口
Java迭代 : Iterator和Iterable接口 从英文意思去理解 Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的.able结尾的表示 能...样,可以做.... ...
- swift使用查阅资料备份1
SnapKit RxSwift R.swift https://www.jianshu.com/p/68e12b966d86 iOS - RxSwift 项目实战记录 https://blog.csd ...
- iOS开发中的错误整理,百思项目'我的'模块,tableFooterViewHeight的问题.提醒自己对KVO和Block的运用欠缺
一.错误分析:由于tableFooterView中的数据是通过请求服务器后得到的,tableFooterViewHeight也是根据请求过来的数据经过布局子控件而计算出来的.(注意:计算高度是在子线程 ...
- 【吐血整理】svn命令行,Subversion的正确使用姿势~
一.写在前面 前面一直博主一直用svn的桌面版本,但看项目经理一直都用的命令行方式,不为性能,还能直接装逼呀!在这里先感谢赵哥,也把它分享给感兴趣的你们~ 二.直接上干货 1. svn checkou ...
- 【吐血整理】SVN命令行,Subversion的正确使用姿势,让版本控制更简单~
一.写在前面 前面一直博主一直用svn的桌面版本,但看项目经理一直都用的命令行方式,不为性能,还能直接装逼呀!在这里先感谢赵哥,也把它分享给感兴趣的你们~ 二.直接上干货 1. svn checkou ...
- 我收录整理的优秀OC技术类文章
自定义导航按钮UIBarButtonItem 关于导航栏的六个小技巧 ios开发的一些小技巧篇一 制作一个可以滑动操作的 Table View Cell - IOS - 伯乐在线 一个 ...
- 深入剖析通知中心和KVO
深入剖析通知中心和KVO 要先了解KVO和通知中心,就得先说说观察者模式,那么观察者模式到底是什么呢?下面来详细介绍什么是观察者模式. 观察者模式 -A对B的变化感兴趣,就注册成为B的观察者,当B发生 ...
随机推荐
- Javascript 中 atob/btoa
解决 Javascript 中 atob 方法解码中文字符乱码问题 由于一些网络通讯协议的限制,你必须使用 window.btoa() 方法对原数据进行编码后,才能进行发送.接收方使用相当于 wind ...
- CVE-2017-6920 Drupal远程代码执行漏洞学习
1.背景介绍: CVE-2017-6920是Drupal Core的YAML解析器处理不当所导致的一个远程代码执行漏洞,影响8.x的Drupal Core. Drupal介绍:Drupal 是一个由 ...
- spring中增加自定义配置支持
spring.schemas 在使用spring时,我们会首先编写spring的配置文件,在配置文件中,我们除了使用基本的命名空间http://www.springframework.org/sche ...
- html中行级元素的居中显示。
垂直居中.以label标签为例. <style> #label1{ vertical-align:middle; line-height:40px;<*父元素的height*> ...
- Expression Blend实例中文教程(3) - 布局控件快速入门Grid
上一篇对Blend 3开发界面进行了快速入门介绍,本篇将基于Blend 3介绍Silverlight控件.对于微软开发工具熟悉的朋友,相信您很快就熟悉Blend的开发界面和控件. XAML概述 Sil ...
- express常用中间件
整理一下工作中经常使用到的Express中间件 config-lite: 读取配置文件 不同环境下配置文件使用 - Node实战 config-lite express-session: sessio ...
- 缓存框架EhCache的简单使用
缓存框架EhCache的简单使用: 1.Spring和EhCache框架整合 1.1导入jar包 <dependencies> <dependency> <groupId ...
- shell输入与输出功能
一.shell输入功能 1. 2. 二.shell输出功能 1.字符界面前景颜色 2.字符界面背景颜色 3.其他输出命令 ①cat 输出文本,将文本的格式也输出 ②tee 既输出,也保存到文件里 ③m ...
- HDU4652:Dice
题面 传送门 题意 \(m\)面的骰子 求连续出现\(n\)个相同面的期望次数 或者 求连续出现\(n\)个不同面的期望次数 Sol 设\(f[i]\)表示已经出现了\(i\)~\(n\)这些面相同的 ...
- Web前端面试指导(十七):一个满屏 品 字布局 如何设计?
题目点评 这道题目有可能是笔试题,有可能面谈的时候进行叙述,如果是笔试题要求对css样式代码非常熟练,如果是面谈叙述,就需要你的表达能力非常强,要抓住要点,把需要用到的技能点讲清楚就可以了. 需要用到 ...