cocoa中我们常用的一种设计模式称为观察者模式(Observer)。它可以在对象之间建立一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖与它的对象都可以得到通知从而调用方法去更新。这一模式中关键对象是目标(或被观察者)和观察者(Observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。每个观察者都将查询目标以使其状态与目标的状态同步。
比如说:用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。
观察者模式的适用性:
存在以下情况可以使用观察者模式。
1.当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使用它们各自独立地改变和复用。
2.当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象待改变。
3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁,也就是说,你不希望这些对象是紧密耦合的。
观察者模式的参与者:
1)被观察的目标(Subject或Observed)
目标知道它的观察者,可以有多个观察者观察同一个目标,由你注册所决定的。
提供注册和删除观察者对象的接口。
2)观察者
为那些再目标发生改变时候需获得通知的对象定义一个更新的接口,比如调用某个方法去刷新。
下面举个简单的例子。
在cocoa中会用NSNotificationCenter这个类。在程序中这个类提供了一种广播通信的机制。
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
这个方法就是给NSNotificationCenter的实例对象注册一个观察者。NSNotificationCenter采用了单例模式生成了一个唯一的实例对象,通过类方法+ (id)defaultCenter可以获取这个唯一实例。
第一个参数id类型:不能为nil。这是目标对象注册的一个观察者。所以前面说过目标是知道它的观察者的。
第二个参数SEL : 指定方法名,要么不给参数,要给参数也只能给一个。
第三个参数NSString*类型: 消息名称
第四个参数:观者者想要接收消息的对象。
- (void)postNotificationName:(NSString*)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo
使用name创建消息,发送,携带信息,传递给接受者。第一个参数:消息名称
第二个参数:传递消息的对象
第三个参数:消息携带的信息,可以是nil的。
- (void)removeObserver:(id)notificationObserver
移除注册。
参数:之前传的观察者对象。
kvo机制也是运用了观察者模式。
KVO:全称Key Value Observer,基于键值的观察者。
一个对象的某一个成员变量,它的变量名可以当作为key。通过kvc可以去设置那个变量的值,得到变量的值,不用通过自己定义的属性和方法而达到修改值的目的。
一个对象的某一个成员变量可以给它增加一个观察者对象。一旦这个被观察的对象的那个变量发生了改变,那么观察者就会接受到消息。
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
这个是注册,给一个对象增加一个观察者对象。第一个参数:观察者对象
第二个参数: 需要观察的键值,或者键路径
第三个参数:观察的选项。比如新值和旧值,NSKeyValueObservingOptionNew,观察新值,NSKeyValueObservingOptionOld,观察旧值。通常是两者都要。传入参数可以这样 NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
第四个参数:一般给nil
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
观察者对象接收消息的方法。一旦被观察对象的那个被观察的属性发生了改变,那么就会触发这个方法。新值和旧值都保存在change的字典里面。- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
移除观察者对象,参数一就是之前注册的观察者对象。参数二就是被观察者的键路径
看看一个简单的程序实例吧:
Book类
#import <Foundation/Foundation.h>
@interface Book : NSObject
{
NSString* name;
float price;
}
@end
Book类的实现
#import "Book.h"
@implementation Book
@end
----------------------------------------------------定一个myClass的类,继承NSObject。
myClass的声明。
#import <Foundation/Foundation.h>
@class Book;
@interface myClass : NSObject
{
Book* book;
}
myClass的实现
#import "myClass.h"
@implementation myClass
-(id)init:(Book *)theBook
{
if (self=[superinit]) {
book=theBook;
[bookaddObserver:selfforKeyPath:@"price"
options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew
context:nil]; //监控新旧price
}
return self;
}
-(void)dealloc
{
[bookremoveObserver:selfforKeyPath:@"price"];//移除去监控
[super dealloc];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{
if([keyPath isEqual:@"price"]){
NSLog(@">>>>>>>price is changed");
NSLog(@"old price is %@",[changeobjectForKey:@"old"]);
NSLog(@"new price is %@",[changeobjectForKey:@"new"]);
}
}
@end
代码分析:
给Book类的一个实例book,它有个成员变量。float price 。myClass注册成为Book类的观察者,通过myClass观察book类的price变化。一旦price发生了改变,那么myClass这个类就会触发下面这个方法,从而得到新值和旧值。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
观察者模式是一个很重要的设计模式。在cocoa中也常用到。也是类与类之间通信的一种常见的方式。