本文主要是介绍KVC、KVO、NSNotification,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
作者:杨登峰 (dengfengyang@gmail.com)
时间:2010-03-12
转帖请注明转之 苹果开发中文网(http://www.cocoadev.cn/Objective-C/KVO-20100222-0627.asp)
以上说明是本文不可分割的一部分。
Objective-C里面的Key-Value Observing (KVO)机制,非常不错,可以很好的减少浇水代码。关于KVO的学习,可以参考文章:《Key-Value Observing快速入门》:http://www.cocoadev.cn/Objective-C/Key-Value-Observing-Quick-Start-cn.asp
关于如何找到实现函数的指针,可参考文章:《Objective-C如何避免动态绑定,而获得方法地址》:
http://www.cocoadev.cn/Objective-C/Get-method-address.asp
参考:http://www.cocoachina.com/macdev/cocoa/2009/0611/221.htm
http://www.cnblogs.com/dark-angel/archive/2011/05/05/2037734.html
http://www.cnblogs.com/pengyingh/articles/2367374.html
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
KVC(key value coding) 是一种间接访问对象属性(用字符串表征)的机制,而不是直接调用对象的accessor方法或是直接访问成员对象。 KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa-swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实际上包含了指向实现类中的方法的指针,和其它数据。
1.KVC来获取/设置属性:
key就是确定对象某个值的字符串,它通常和accessor方法或是变量同名,
并且必须以小写字母开头。Key path就是以“.”分隔的key,因为属性值也能包含属性。
2.接下来要说明下实现KVC的访问器方法(accessor method)。
Apple给出的惯例通常是:
(1)-- [site setValue:@"sitename" forKey:@"name"];(使用的name convention和setter/getter命名一致)。
就会被编译器处理成:
SEL sel = sel_get_uid ("setValue:forKey:"); SEL数据类型:它是编译器运行Objective-C里的方法的环境参数。
IMP method = objc_msg_lookup (site->isa,sel); IMP数据类型:他其实就是一个 编译器内部实现时候的函数指针。
当Objective-C编译器去处理实现一个方法的时候,就会指向一个IMP对象,
这个对象是C语言表述的类型(在Objective-C的编译器处理的时候,基本上都是C语言的)。
method(site, sel, @"sitename", @"name");
对于未定义的属性可以用setNilValueForKey:
这下KVC内部的实现就很清楚的清楚了:一个对象在调用setValue的时候,首先根据方法名找到运行方法的时候所需要的环境参数。
他会从自己isa指针结合环境参数,找到具体的方法实现的接口。再直接查找得来的具体的方法实现
(2)--[site valueForKey:@"name": 当通过KVC调用对象时,比如:[self valueForKey:@”name”]时,
程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 name 这个方法,
如果没找到,会继续查找对象是否带有name这个实例变量(iVar),
不仅仅会查找name这个方法,还会查找getname这个方法,前面加一个get,或者_name以及_getname这几种形式。
同时,查找实例变量的时候也会不仅仅查找name这个变量,也会查找_name这个变量是否存在.
如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。
如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.KVO (key value observing)
1.机制概述
当指定的对象的属性被修改了,允许对象接受到通知的机制。每次指定的被观察对象的属性被修改的时候,KVO都会自动的去通知相应的观察者.
2.优点
这样的架构有很多好处。首先,开发人员不需要自己去实现这样的方案:每次属性改变了就发送消息通知。这是KVO机制提供的最大的优点。因为这个方案已经被明确定义,获得框架级支持,可以方便地采用。开发人员不需要添加任何代码,不需要设计自己的观察者模型,直接可以在工程里使用。其次,KVO的架构非常的强大,可以很容易的支持多个观察者观察同一个属性,以及相关的值。
3.如何工作
需要三个步骤来建立一个属性的观察员。理解这三个步骤就可以知道KVO如何设计工作的。
(1)首先,构思一下如下实现KVO是否有必要。比如,一个对象,当另一个对象的特定属性改变的时候,需要被通知到。
例如,PersonObject希望能够觉察到BankObject对象的accountBalance属性的任何变化。
(2)那么PersonObject必须发送一个“addObserver:forKeyPath:options:context:”消息,注册成为BankObject的accountBalance属性的观察者。
keyPath就是要观察的属性值,options给你观察键值变化的选择,而context方便传输你需要的数据(注意这是一个void型)
(说明:“addObserver:forKeyPath:options:context:”方法在指定对象实例之间建立了一个连接。注意,这个连接不是两个类之间建立的,而是两个对象实例之间建立的。)
(3)为了能够响应消息,观察者必须实现“observeValueForKeyPath:ofObject:change:context:”方法。这个方法实现如何响应变化的消息。在这个方法里面我们可以跟自己的情况,去实现应对被观察对象属性变动的相应逻辑。
change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
(4)假如遵循KVO规则的话,当被观察的属性改变的话,方法“observeValueForKeyPath:ofObject:change:context:”会自动被调用。
4KVO总结:
当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。
只有当我们调用KVC去访问key值的时候KVO才会起作用。所以肯定确定的是,KVO是基于KVC实现的。其实看了上面我们的分析以后,关系KVO的架构的构思也就水到渠成了。
因为KVC的实现机制,可以很容易看到某个KVC操作的Key,而后也很容易的跟观察者注册表中的Key进行匹对。假如访问的Key是被观察的Key,那么我们在内部就可以很容易的到观察者注册表中去找到观察者对象,而后给他发送消息。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NSNotificationCenter
- (NSString *)name;
- (id)object;
- (NSDictionary *)userInfo;
作用:NSNotificationCenter是专门供程序中不同类间的消息通信而设置的.
注册通知:即要在什么地方接受消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mytest:) name:@" mytest" object:nil];
参数介绍:
addObserver: 观察者,即在什么地方接收通知;
selector: 收到通知后调用何种方法;
name: 通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的。
发送通知:调用观察者处的方法。
[[NSNotificationCenter defaultCenter] postNotificationName:@"mytest" object:searchFriendArray];
参数:
postNotificationName:通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的。
object:传递的参数
注册方法的写法:
- (void) mytest:(NSNotification*) notification
{
id obj = [notification object];//获取到传递的对象
}
4.移除通知:removeObserver:和removeObserver:name:object:
其中,removeObserver:是删除通知中心保存的调度表一个观察者的所有入口,而removeObserver:name:object:是删除匹配了通知中心保存的调度表中观察者的一个入口。
这个比较简单,直接调用该方法就行。例如:
[[NSNotificationCenter defaultCenter] removeObserver:observer name:nil object:self];
注意参数notificationObserver为要删除的观察者,一定不能置为nil。
PS:这里简单说一下通知中心保存的调度表。通知中心的调度表是给一些观察者指定的一些通知集。一个通知集是通知中心发出的通知的子集。每个表的入口包含:
通知观察者(必须要的)、通知名称、通知的发送者。
下图表示通知集中指定的通知的调用表入口的四种类型:
下图表示四种观察者的调度表
最后,提醒一下观察者收到通知的顺序是没有定义的。同时通知发出和观察的对象有可能是一样的。通知中心同步转发通知给观察者,就是说 postNotification: 方法直到接收并处理完通知才返回值。要想异步的发送通知,可以使用NSNotificationQueue。在多线程编程中,通知一般是在一个发出通知的那个线程中转发,但也可能是不在同一个线程中转发通知。
这篇关于KVC、KVO、NSNotification的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!