·您现在的位置: 江北区云翼计算机软件开发服务部 >> 文章中心 >> 网站建设 >> app软件开发 >> IOS开发 >> 读书笔记-常用设计模式之观察者模式

读书笔记-常用设计模式之观察者模式

作者:佚名      IOS开发编辑:admin      更新时间:2022-07-23

1、观察者(Observer)模式也叫发布/订阅(Publish/Subscribe)模式,是MVC(模型-视图-控制器)模式的重要组成部分。在软件系统中,一个对象状态改变也会连带影响其他很多对象的状态发生改变。能够实现这一需求且复用性强,对象之间匿名通信的,观察者模式是其中最适合的一个。

2、观察者模式的类图如下:

 它有四个角色:

抽象主题(Subject):在Objective-C中,抽象主题是一个协议,它是观察者集合容器,定义了添加观察者(attach)方法、移除观察者(detach)方法和为所有观察者发送通知的方法(notifyObserver)。

抽象观察者(Observer):在OC中,抽象观察者是一个协议,它是一个更新(update)方法。

具体观察者(ConcreteObserver):Observer协议的具体实现。

具体主题(ConcreteSubject):Subject协议的具体实现。

引入Subject和Observer这两个协议后,不仅提高了系统的可复用性,还降低了耦合度。

抽象观察者(Observer)和抽象主题(Subject)的实现代码如下:

 1 //
 2 //Observer.h 
 3 //ObserverPattern 
 4 //
 5 @PRotocol Observer 
 6 @required 
-(void)update; 7 @end 8 9 // 10 //Subject.h 11 //ObserverPattern 12 // 13 @class Observer; 14 @protocol Subject 15 @required 16 -(void)attach:(Observer*) observer; 17 -(void)detach:(Observer*) observer; 18 -(void)notifyObservers; 19 @end

具体观察者(ConcreteObserver)的实现代码如下:

//
//ConcreteObserver.h
//ObserverPattern
//
#import "Observer.h"
@interface ConcreteObserver : NSObject <Observer> 
@end

//
//ConcreteObserver.m
//ObserverPattern
//
#import "ConcreteObserver.h"
@implementation ConcreteObserver
//只是做了个输出处理
-(void)update
{
        NSLog(@"ConcreteObserver : %@",self);
 }
@end

下面是具体主题(ConcreteSubject)的实现代码:

//
//ConcreteSubject.h
//ObserverPattern
//
#import "Subject.h"
@class Observer;
@interface ConcreteSubject : NSObject <Subject>
{
        NSMutableArray* observers;
}
@property (nonatomic ,strong) NSMutableArray* observers;
+(ConcreteSubject*)sharedConcreteSubject;
@end

//
//ConcreteSubject.m 
//ObserverPattern
//
#import "ConcreteSubject.h" 
@implementation ConcreteSubject

@synthesize observers;
static ConcreteSubject *sharedConcreteSubject = nil;
+(ConcreteSubject*)sharedConcreteSubject
{
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            sharedConcreteSubject = [[self alloc] init];
            sharedConcreteSubject.observers = [[NSMutableArray alloc] init]; });
        return sharedConcreteSubject;
}
-(void)attach:(Observer*) observer
{
        [self.observers addObject:observer];
}
-(void)detach:(Observer*) observer
{
        [self.observers removeObject:observer];
}
-(void)notifyObservers
{
        for (id obs in self.observers)
        {
           [obs update];
        }
} 

@end

因为ConcreteSubject只需要一个实例,所以我们采用单例设计模式实现。

3、通知机制和KVPO机制

在Cocoa Touch框架中,观察者模式的具体应用有两个-通知(notification)机制和KVO(Key-Value-Observing)机制。

3.1、通知机制

通知机制和委托机制不同的是,前者是“一对多”的对象之间的通信,后者是“一对一”的对象之间的通信。

如图,在通知机制中对某个通知感兴趣的所有对象都可以成为接收者。首先,这些对象需要向通知中心(NSNotificationCenter)发出addObserver:selector:name:object:消息进行注册,在投送对象投送通知给通知中心时,通知中心就会把通知广播给注册过的接收者。所有的接收者都不知道通知是谁投送的,更不关系它的细节。投送对象与接收者是一对多的关系。接收者如果对通知不再关注,会给通知中心发出removeObserver:name:object:消息接触注册,以后不再接收通知。

代码示例:主界面控制器MainViewController和翻转界面控制器FilpsideViewController,此外还有应用程序委托对象AppDelegate。我们将两个视图控制器座位通知的接收者,应用程序委托对象作为通知投送对象。

在MainViewController和FilpsideViewController这两个视图控制器中,注册通知接收者的代码如下:

1 - (void)viewDidLoad
2 {
3     [super viewDidLoad];
4     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTerminate:) name:@"AppWillTerminateNotification" object:nil];
5 }

解除注册代码类似,通过NSNotificationCenter发出removeObserver消息实现。对于一般的OC对象可以在dealloc方法中发出消息。对于视图控制器,也可以在didReceiveMemoryWarning方法或viewDidUnload方法中发出消息。

1 - (void)dealloc
2 {
3 [[NSNotificationCenter defaultCenter] removeObserver:self];
4 }

MainViewController和FilpsideViewController处理通知的方法基本相同

1 #pragma mark - 􏴾理通知 
2 -(void)handleTerminate:(NSNotification*)notification
3     {
4         NSDictionary *theData = [notification userInfo];
5         if (theData != nil) {
6             NSDate *date = [theData objectForKey:@"TerminateDate"];
7             NSLog(@"FlipsideViewController App Terminate Date: %@", date);
8         }
9 }

上面的方法可以接收一个NSNotification的参数。NSNotification类中有3个重要的属性--name、object和userinfo,这3个属性与通知中心投送方法中的参数有一定得对应关系。

其中name是通知的名字,object是投送通知时传递过来的对象,userinfo是投送通知时定义的字典对象,可借助于该参数传递数据。

如果我们想在应用程序终止时投送通知,需要重写AppDelegate中的方法applicationWillTerminate:,代码如下

 1 - (void)applicationWillTerminate:(UIApplication *)application
 2 {
 3       NSDate *date = [NSDate date];
 4       NSDictionary *dataDict = [NSDictionary dictionaryWithObject:date
 5             forKey:@"TerminateDate"];
 6       [[NSNotificationCenter defaultCenter]
 7             postNotificationName:@"AppWillTerminateNotification"
 8             object:self
 9             userInfo:dataDict];
10 }

我们在applicationWillTerminate:方法中投送一个应用终止通知,事实上,Cocoa Touch框架提供了通知,当程序终止事件发生时,由iOS系统自动投送,使用iOS系统自动投送通知后,我们需修改AppDelegate的applicationWillTerminate:方法,删除其中的代码,修改MainViewController和FildsideViewController中注册通知接收者的代码,具体如下:

1 - (void)viewDidLoad
2 {
3         [super viewDidLoad];
4         [[NSNotificationCenter defaultCenter] addObserver:self
5             selector:@selector(handleTerminate:)
6             name:UIApplicationWillTerminateNotification
7             object:nil];
8 }

这里把通知的name由“AppWillTerminateNotification”修改为UIApplicationWillTerminateNotification,这是为了能够接收UIApplicationWillTerminateNotification通知。

3.2、KVO

KVO不像通知机制那样通过一个通知中心通知所有观察者对象,而是在对象属性变化时通知会被直接发送给观察者对象。

可以看到,属性发生变化的对象需要发出消息addObserver:forKeyPath:options:context:给注册观察者,使观察者关注它的某个属性的变化。当对象属性变化时,观察者就会接收到通知,观察者需要重写方法observerValueForKeyPath:ofobject:change:context:以响应属性的变化。

实例:我们使用KVO机制来监视应用程序的状态的变化。应用程序委托对象的AppDelegate.h文件的代码如下:

1 @interface AppDelegate : UIResponder <UIApplicationDelegate>
2 @property (strong, nonatomic) UIWindow *window;
3 @property (strong, nonatomic) NSString *appStatus;
4 @property (strong, nonatomic) AppStatusWatcher *watcher;
5 @end

其中appStatus属性用来记录应用程序状态的变化,AppStatusWatcher *watcher是定义AppStatusWatcher类型的观察者对象属性。AppDelegate.m的代码如下:

 1 @implementation AppDelegate
 2 - (BOOL)application:(UIApplication *)application
 3 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 4 {
 5         self.watcher = [AppStatusWatcher new];
 6         [self addObserver:self.watcher forKeyPath:@"appStatus"
 7             options:NSKeyValueObservingOptionNew |
 8             NSKeyValueObservingOptionOld context:@"Pass Context"];
 9         self.appStatus = @"launch";
10 return YES;
11 }
12 
13 - (void)applicationWillResignActive:(UIApplication *)application
14 {
15        self.appStatus = @"inactive";
16 }
17 - (void)applicationDidEnterBackground:(UIApplication *)application
18 {
19         self.appStatus = @"background";
20 }
21 - (void)applicationWillEnterForeground:(UIApplication *)application
22 {
23         self.appStatus = @"inactive";
24 }
25 - (void)applicationDidBecomeActive:(UIApplication *)application
26 {
27         self.appStatus = @"active";
28 }
29 - (void)applicationWillTerminate:(UIApplication *)application
30 {
31         self.appStatus = @"terminate";
32 } 
33 
34 @end

其中application:didFinishLaunchingOptions:方法通过addObserver:forKeyPath:options:context:语句注册观察者,其中参数addObserver是要被关注的对象,froKeyPath是被关注对象的属性。options是为属性变化设置的选项,本例中被设定为 NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld ,把属性新旧两个值都传递给观察者。context参数是上下文内容,类型为(void*)(C语言形式的任何指针类型),因此,如果传递“空”,应该是“NULL”而非“nil”。

观察者AppStatusWatcher的代码如下:

 1 @interface AppStatusWatcher : NSObject
 2 @end
 3 
 4 @implementation AppStatusWatcher
 5 
 6 - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object
 7         change:(NSDictionary*)change context:(void*)context
 8 {
 9         NSLog(@"Property '%@' of object '%@' changed: %@ context: %@",keyPath,object,change,context);
10 } 
11 @end

因为NSObject类实现了NSKeyValueObserving协议,所以只需声明AppStatusWatcher继承了NSObject类,而无需实现NSKeyValueObserving协议。

observeValueForKeyPath:ofobject:change:context:方法的observeValueForKeyPath参数是被关注的属性。ofobject是被关注的对象,change是字典类型,包含了属性变化的内容,这些内容与注册时属性变化设置的选项(option参数)有关。context是注册时传递的上下文内容。

 

资料:《iOS开发指南》