键值观察 KVO
http://www.cnblogs.com/dyf520/p/3805297.html
Key-Value Observing Programming Guide
1,注册Key-Value Observing: 要实现这个目的,需要:
1)被观察的类对你想要观察的属性必须是服从Key-Value observing的
2)你必须注册被观察对象的观察对象,使用addObserver:forKeyPath:options:context:.
3)观察者类必须实现observeValueForKeyPath:ofObject:change:context:
重要提示:不是所有的类对所有的属性都服从KVO。你必须确保你拥有的类是服从KVO的
2,注册为一个观察者:使用addObserver:forKeyPath:options:context:.
使用选项NSKeyValueObservingOptionOld来指定初始对象值在change字典中提供给观察者。
使用NSKeyValueObservingOptionNew选项来通过change字典提供新值。
要项获得这两个值,需要按位OR这两个选项常量。
当你注册一个对象为观察者时,你还可以提供一个上下文指针。当observeValueForKeyPath:ofObject:change:context被调用时,上下文指针提供给观察者。上下文指针可以是一个C指针或一个对象引用。上下文指针可以被用来作为一个唯一的标识来确定被观察的更改,或提供其他一些数据给观察者。
提示:KVO addObserver:forKeyPath:options:context:方法不维系强引用到观察对象、被观察对象或上下文。你应该确保你维系强引用到观察、被观察、对象和上下文,如果必要的话。
3,接收更改通知:
当被观察的对象的属性更改时,观察者接收一个observeValueForKeyPath:ofObject:change:context:消息。所有的观察者必须实现这个方法。
Change字典入口NSKeyValueChangeKindKey提供发生的更改类型的信息。如果被观察对象的值发生了更改,NSKeyValueChangeKindKey入口返回NSKeyValueChangeSetting。基于观察者注册的options, NSKeyValueChangeOldKey和NSKeyValueChangeNewKey入口为属性更改前和更改后的值。如果其值为标量,会自动用NSValue和NSNumber包裹。
如果被观察的属性是一个多值属性,NSKeyValueChangeKindKey入口仍然指示对象是被添加、移除或被替换,通过返回NSKeyValueChangeInsertion、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement来标识。
Change字典的入口NSKeyValueChangeIndexesKey是一个NSIndexSet对象来指定更改的indexes。如果NSKeyValueObservingOptionVew或NSKeyValueObservingOptionOld被注册,NSKeyValueChangeOldKey和NSKeyValueChangeNewKey入口是arrays包含相关对象的更改前的值和更改后的值。
Listing 2 Implementation of observeValueForKeyPath:ofObject:change:context:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqual:@"openingBalance"]) {
[openingBalanceInspectorField setObjectValue:
[change objectForKey:NSKeyValueChangeNewKey]];
}
/*
Be sure to call the superclass's implementation *if it implements it*.
NSObject does not implement the method.
*/
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
4,移除观察者对象:
removeObserver:forKeyPath:消息给被观察对象。例子:
[observedObject removeObserver:inspector forKeyPath:@"openingBalance"];
如果上下文是一个对象,你必须对其保持一个强引用直到你移除观察者。
5,KVO Compliance:
1)类必须是符合KVC
2)类为属性发出KVO更改通知
3)基于的keys被适当地注册。
有两种机制来确保更改通知被发出。NSObject的自动支持并被默认可用于类所有遵循KVC的属性。
当通知被发出时,手动更改通知提供附加控制,并且需要额外的代码。你可以控制自动通知属性,通过子类化并实现automaticallyNotifiesObserversForKey:方法。
6,自动更改通知:
例如,下面的方法引发KVO更改通知发出。
//调用访问方法
[account setName:@"Savings"];
// 使用 setValue:forKey:.
[account setValue:@"Savings" forKey:@"name"];
// 使用 key path, where 'account' is a kvc-compliant property of 'document'.
[document setValue:@"Savings" forKeyPath:@"account.name"];
// 使用 mutableArrayValueForKey: to retrieve a relationship proxy object.
Transaction *newTransaction = <#Create a new transaction for the account#>;
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject:newTransaction];
7,手动更改通知:
一个实现手动更改通知的类必须实现automaticallyNotifiesObserversForKey:方法。它可能同时使用自动通知和手动通知。对于其要进行手动通知的属性,他应该在automaticallyNotifiesObserversForKey:方法中将其设置为NO。如下所示:
Listing 2 Example implementation of automaticallyNotifiesObserversForKey:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"openingBalance"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
要实现手动通知,在更改之前你调用willChangeValueForKey:,在更改之后调用didChangeValueForKey:。
Listing 3 Example accessor method implementing manual notification
- (void)setOpeningBalance:(double)theBalance {
[self willChangeValueForKey:@"openingBalance"];
_openingBalance = theBalance;
[self didChangeValueForKey:@"openingBalance"];
}
你可以最小化发送不必要的通知,通过首先检查值是否发生变化。
Listing 4 Testing the value for change before providing notification
- (void)setOpeningBalance:(double)theBalance {
if (theBalance != _openingBalance) {
[self willChangeValueForKey:@"openingBalance"];
_openingBalance = theBalance;
[self didChangeValueForKey:@"openingBalance"];
}
}
如果一个单独的操作引起多个键的值发生变化,你必须nest(筑巢)更改通知,像下面这样:
Listing 5 Nesting change notifications for multiple keys
- (void)setOpeningBalance:(double)theBalance {
[self willChangeValueForKey:@"openingBalance"];
[self willChangeValueForKey:@"itemChanged"];
_openingBalance = theBalance;
_itemChanged = _itemChanged+1;
[self didChangeValueForKey:@"itemChanged"];
[self didChangeValueForKey:@"openingBalance"];
}
对于一个有序的多值关联,除了指定更改的键,你还需要指定更改的类型和关联的indexes。更改的类型是一个NSKeyValueChange来标识NSKeyValueChangeInsertion、NSKeyValueChangeRemoval或NSKeyValueChangeReplacement。受影响的indexes通过NSIndexSet被传递。
Listing 6 Implementation of manual observer notification in a to-many relationship
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
[self willChange:NSKeyValueChangeRemoval
valuesAtIndexes:indexes forKey:@"transactions"];
// Remove the transaction objects at the specified indexes.
[self didChange:NSKeyValueChangeRemoval
valuesAtIndexes:indexes forKey:@"transactions"];
}
8,注册Dependent Keys:
有很多时候一个属性的值基于另外一个对象的的一个或多个属性。如果一个属性的值发生更改,然后衍生的属性也应该被标志为更改。你将如何确保在这些属性发生更改时,基于它的属性通过KVO通知被发布?
1)对于单一关联:
你需要重载keyPathsForValuesAffectingValueForKey:或实现一个合适的方法
例如,一个人的全名基于first name和last name。一个方法返回其全名:
- (NSString *)fullName {
return [NSString stringWithFormat:@"%@ %@",firstName, lastName];
}
一个应用观察fullName属性,必须在firstName和lastName属性发生变化时得到通知。
一个解决办法是重载keyPathsForValuesAffectingValueForKey:来指定fullName属性基于lastName和firstName。如下所示:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"fullName"]) {
NSArray *affectingKeys = @[@"lastName", @"firstName"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
你的重载需要调用super的方法,并返回一个set。
你还可以得到同样的结果,通过实现一个类方法 keyPathsForValuesAffecting<Key>,<key>是属性的名字,第一个字母为大写,如下所示:
+ (NSSet *)keyPathsForValuesAffectingFullName {
return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}
当你使用类别(category)向一个已存在的类添加一个计算出来的属性时,你不能重载keyPathsForValuesAffectingValueForKey:方法,因为你不能在类别中重载方法。在这种情况中,实现一个符合的keyPathsForValuesAffecting<Key>类方法来获得这种机制的好处。
2)对于多值关联:
keyPathsForValuesAffectingValueForKey:方法不支持包含多值关联的key-paths。例如,假设你有一个部门对象,有很多职员(employees),并且职员有工资属性。你可能想部门对象有一个totalSalary属性,基于所有职员的工资.你不能keyPathsForValuesAffectingTotalSalary并返回employees.salary。
这里有两种可能的解决方法:
a) 使用KVO来注册其父(这个例子里是部门)作为其所有子(这里是职员)对象的相关属性的观察者。你必须在添加或移除子对象时,添加或移除父对象作为观察者。在其observeValueForKeyPath:ofObject:change:context:方法中,你需要更新相应的值来响应通知,并发出通知。如下所示:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == totalSalaryContext) {
[self updateTotalSalary];
}
else
// deal with other observations and/or invoke super...
}
- (void)updateTotalSalary {
[self setTotalSalary:[self valueForKeyPath:@"employees.@sum.salary"]];
}
- (void)setTotalSalary:(NSNumber *)newTotalSalary {
if (totalSalary != newTotalSalary) {
[self willChangeValueForKey:@"totalSalary"];
_totalSalary = newTotalSalary;
[self didChangeValueForKey:@"totalSalary"];
}
}
- (NSNumber *)totalSalary {
return _totalSalary;
}
b) 如果你正在使用Core Data,你可以注册父对象到application’s notification center作为其管理的对象的上下文观察者。父对象应该响应子对象发布的相应的更改通知。
键值观察 KVO的更多相关文章
- [原创]obj-c编程17:键值观察(KVO)
原文链接:[原创]obj-c编程17:键值观察(KVO) 系列专栏链接:objective-c 编程系列 说完了前面一篇KVC,不能不说说它的应用KVO(Key-Value Observing)喽.K ...
- OC键值观察KVO
什么是KVO? 什么是KVO?KVO是Key-Value Observing的简称,翻译成中文就是键值观察.这是iOS支持的一种机制,用来做什么呢?我们在开发应用时经常需要进行通信,比如一个model ...
- obj-c编程17:键值观察(KVO)
说完了前面一篇KVC,不能不说说它的应用KVO(Key-Value Observing)喽.KVO类似于ruby里的hook功能,就是当一个对象属性发生变化时,观察者可以跟踪变化,进而观察或是修正这个 ...
- 路径(keyPath)、键值编码(KVC)和键值观察(KVO)
键路径 在一个给定的实体中,同一个属性的所有值具有相同的数据类型. 键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制. - 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接 ...
- 09 (OC)* 键路径(keyPath)、键值编码(KVC)、键值观察(KVO)
键路径在一个给定的实体中,同一个属性的所有值具有相同的数据类型.键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制. - 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一 ...
- 《苹果开发之Cocoa编程》键-值编码和键-值观察
一.KVC 键-值编码(Key - Value Coding, KVC)是通过变量名的读取和设置变量值的一种方法,将字符串的变量名作为key来引用.NSObject定义了两个方法(KVC方法)用于变量 ...
- K-V-O 键值观察机制
在两个不同的控制器之间传值是iOS开发中常有的情况,应对这种情况呢,有多种的应对办法.kvc就是其中的一种,所以,我们就在此解释之. key value observing 键值观察,给人一种高 ...
- iOS - KVO 键值观察
1.KVO KVO 是 Key-Value Observing 的简写,是键值观察的意思,属于 runtime 方法.Key Value Observing 顾名思义就是一种 observer 模式用 ...
- [深入浅出Cocoa]详解键值观察(KVO)及其实现机理
一,前言 Objective-C 中的键(key)-值(value)观察(KVO)并不是什么新鲜事物,它来源于设计模式中的观察者模式,其基本思想就是: 一个目标对象管理所有依赖于它的观察者对象,并在它 ...
随机推荐
- viewcontroller生命周期知识要点
一 viewcontroller执行方法的主要顺序为: init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—> ...
- saltstack安装配置(syndic)
syndic是saltstack用来做集群部署的,一般结构如图: syndic是一个特殊的minion,syndic类继承于minion类,syndic可以看作一个代理,只做数据传递. CentOS上 ...
- IBM中国研究院、SAP、网易游戏、IBM2015应届生招聘笔试面试问题分享
IBM中国研究院实习生 早在今年4月份.我面试的是IBM中国研究院的实习生岗位.主要是自然语言处理和语义网方向.那时我还在香港上学,两个考官对我进行的是电话面试,大概持续半个多小时,首先是我的自我介绍 ...
- [转]XCode中修改缺省公司名称/开发人员名称
本文转载至 http://www.cnblogs.com/zhulin/archive/2011/11/24/2261537.html XCode新建文件后,头部会有开发人员名称,公司名称等信息 ...
- python day-01 (python基础知识1)
1.计算机组成(参与运算的) 主板+CPU+内存 2.python 简史 解释型语言,弱类型语言(eg:a 可以表示很多意思 a=1 或 a = “小红”) 3.数据类型: int(整数类型): ...
- string和int互相转化
1 如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([ ...
- Axure Base 03
(三)Axure rp元件的触发事件 l OnClick(点击时): 鼠标点击事件,除了动态面板的所有的其他元件的点击时触发.比如点击按钮. l OnMouseEnter(鼠标移入时): 鼠标进入 ...
- ubuntu 本地和服务器scp文件传输
安装 SSH(Secure Shell) 服务以提供远程管理服务 sudo apt-get install ssh SSH 远程登入 Ubuntu 机 ssh username@192.168.0.1 ...
- POJ1459 Power Network —— 最大流
题目链接:https://vjudge.net/problem/POJ-1459 Power Network Time Limit: 2000MS Memory Limit: 32768K Tot ...
- 广大暑假训练1 E题 Paid Roads(poj 3411) 解题报告
题目链接:http://poj.org/problem?id=3411 题目意思:N个city 由 m 条路连接,对于一条路(假设连接Cityia和 Cityb),如果从Citya 去 Cityb的途 ...