小鹏的技术博客

求索

Hi,我是小鹏,Java/iOS/Android开发者!


希望在这里记录一些自己对技术的学习和思考,欢迎交流!

设计模式之观察者模式Observer

1.何为观察者模式(Observer)

观察者模式(又被称为发布/订阅模式Publish-Subscribe或依赖模式Dependents)是软件设计模式的一种。即定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在此种模式中,一个目标对象管理所有相依赖于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常通过调用各观察者所提供的方法来实现。此种模式通常被用来实现分布式事件处理系统。此模式也是MVC模式的关键组成部分。在许多编程中都用到了观察者模式,包括几乎所有的GUI程序都用到了此模式,例如:Mac、iOS、Win操作系统的图形用户界面。

观察者模式可能引起内存泄露(memory leaks),著名的流失监听者泄露问题(Lapsed listener problem)此总是经常发生在具备垃圾回收特性的编程中,因为此模式需要基于明确的注册和注销主题对象来实现,并且主题对象持有观察者对象的强引用。而观察者对象也可以称为监听者,主题对象持有所有监听者对象的强引用,导致监听者对象内存无法被释放,造成内存泄露。此问题可以通过将主题对象所持有的强引用改为弱引用的方式来防止内存泄露。

减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。

2.如何实现观察者(Objective-C)

实现思路:主题(Subject)持有所有的观察者列表,而观察者(Observer)监听主题对象。主题对象状态发生变化时,会通知所有持有的观察者对象,使它们能够做出响应。结构如下图:

创建Observer观察者协议:

//
//  Observer.h
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//
#import <Foundation/Foundation.h>

@protocol Observer <NSObject>

-(void) notify:(NSString *)message;

@end

创建Subject主题协议及实现:

//
//  Subject.h
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Observer.h"

@protocol Subject <NSObject>

-(void) addObserver:(id) observer;
-(void) removeObserver:(id) observer;
-(void) notifyObservers:(NSString *) message;

@end

@interface Subject : NSObject <Subject>

@property (nonatomic, strong) NSMutableSet *observerCollection;

@end

//
//  Subject.m
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//

#import "Subject.h"

@implementation Subject

-(NSMutableSet *) observerCollection
{
    if (_observerCollection == nil)
        _observerCollection = [[NSMutableSet alloc] init];
    
    return _observerCollection;
}

-(void) addObserver:(id)observer
{
    [self.observerCollection addObject:observer];
}

-(void) removeObserver:(id)observer
{
    [self.observerCollection removeObject:observer];
}

-(void) notifyObservers:(NSString *)message
{
    for (id<Observer> observer in self.observerCollection) {
        [observer notify:message];
    }
}

@end

创建Observer实现:

//
//  ConcreteObserverA.m
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//

#import "ConcreteObserverA.h"

@implementation ConcreteObserverA

-(void) notify:(NSString *)message
{
    NSLog(@"Class is %@,message is : %@", NSStringFromClass([self class]), message);
}

@end

//
//  ConcreteObserverB.m
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//

#import "ConcreteObserverB.h"

@implementation ConcreteObserverB

-(void) notify:(NSString *)message
{
    NSLog(@"Class is %@ ,message is : %@", NSStringFromClass([self class]), message);
}

@end

main方法中测试代码:

//
//  main.m
//  Observer
//
//  Created by 小鹏 on 14/7/7.
//  Copyright (c) 2014年 ROC. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Subject.h"
#import "ConcreteObserverA.h"
#import "ConcreteObserverB.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Subject *subject = [[Subject alloc] init];
        ConcreteObserverA *observerA = [[ConcreteObserverA alloc] init];
        ConcreteObserverB *observerB = [[ConcreteObserverB alloc] init];
        
        [subject addObserver:observerA];
        [subject addObserver:observerB];
        
        [subject notifyObservers:@"notify event!"];
    }
    return 0;
}

打印结果:

2014-07-07 11:13:30.869 Observer[18209:1539966] Class is ConcreteObserverA,message is : notify event!
2014-07-07 11:13:30.871 Observer[18209:1539966] Class is ConcreteObserverB,message is : notify event!

3.应用(Cocoa中的Observer)

iOS作为主要的GUI系统,大量应用了观察者模式,其中的事件响应机制就是观察者的深度应用和实现。Cocoa框架中也不乏观察者模式应用的例子。通知(NSNotification & NSNotificationCenter)和 KVO(Key-Value Observing)是对观察者模式的实践。

  • NSNotification & NSNotificationCenter
    Cocoa通知机制,遵循广播模型(broadcast model),通过注册成为通知中心的观察者对象,当事件发生时观察者会收到消息并做出响应。这种机制使主题与观察者以一种松耦合的方式通信。主题与观察者在通信时无需了解对方的实现。通过发送消息可以达到代码的彻底解耦。

    任何对象都可以观察通知,但要做到这一点,该对象必须注册,以接收通知。在注册时,必须指定一个选择器,以确定由通知传送所调用的方法;方法签名必须只有一个参数:通知对象。注册后,观察者也可以指定发布对象。通知机制中涉及两个重要的类:
    1)NSNotification: 这是消息的载体,通过它可以把消息内容传递给观察者。
    2)NSNotificationCenter: 实现NSNotificationCenter的原理是一个观察者模式,通过调用静态方法defaultCenter就可以获取这个通知中心的对象,而NSNotificationCenter同时是一个单例模式,这个通知中心的对象会一直存在于一个应用的整个生命周期中。一个NSNotificationCenter可以有许多的通知消息NSNotification,对于每一个NSNotification可以有很多的观察者Observer来接收通知。
    注册成为观察者:
[[NSNotificationCenter defaultCenter] addObserver:self 
										 selector:@selector(downloadImage:) 
										 	 name:@"BLDownloadImageNotification" 
										   object:nil];

发送消息:

[[NSNotificationCenter defaultCenter] postNotificationName:@"BLDownloadImageNotification"
                                                    object:self                                                 
                                                  userInfo:@{@"imageView":coverImage, @"coverUrl":albumCover}];

dealloc中移除观察者:

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
  • KVO
    Cocoa中提供一种称为键值观察的机制,对象可以通过KVO得到其他对象特定属性的变更通知。这种机制在MVC模式的场景中尤为重要。因为它让View对象可以经由Controller对象观察Model对象的变更
    这一机制基于NSKeyValueObserving非正式协议,Cocoa通过这个协议为所有遵循协议的对象提供了一种自动化的属性观察能力。要实现自动观察,参与KVO的对象要符合KVC的要求,并且需要符合KVC的存取方法。KVC基于有关非正式协议,通过存取对象属性实现自动观察。
    Notifications和KVO者是Cocoa对观察者模式的实践。尽管两者依赖同样的发布-订阅关系,但是它们是为不同的解决方案而设计的。两者的主要差别如下表:

    Notifications KVO
    一个中心对象为所有观察者提供变更通知 被观察的对象直接向观察者发送通知
    主要从广义上关注程序事件 绑定于特定对象属性的值
    通知有开销,即使你不使用它们。每次发布一个通知时,它必须对每一个观察系统中进行检查,即使没有人观察该对象(即使没有人正在观察的任何东西) 如果没有实际观察到的任何对象,则零开销

参考:
Observer Pattern
流失监听者泄露
iOS Design Patterns
Key-Value Observer
Patterns in Objective-C: Observer Pattern
Notification Programming Topics
[Key-Value Coding Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.hbr/> Key-Value Observing Programming Guide

最近的文章

设计模式之委托模式Delegation

1.什么是委托模式(Delegation)委托是指给一个对象提供机会对另一个对象中的变化做出反应或者影响另一个对象的行为。即有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。其基本思想是:两个对象协同解决问题。一个对象非常普通,并且打算在广泛的情形中重用。它存储指向另一个对象(即它的委托)的引用,并在关键时刻给委托发消息。消息可能只是通知委托发生了某件事情,给委托提供机会执行额外的处理,或者消息可能要求委托提供一些关键的信息以控制所发生的事情。委托模式是一项基本技巧...…

继续阅读
更早的文章

iOS核心技术之:内存管理之二手动内存管理

1.关于内存管理应用程序内存管理是:“在程序运行时开辟内存空间、使用内存空间,并在程序完成时释放内存空间的过程”。写得好的程序,会尽可能少占用内存。在Objectiv-C中,内存管理被看做是在很多数据和代码中分配受限内存资源所有权(Ownership)的一种方式。虽然内存管理通常被认为是针对单个对象级别进行的,但实际上我们的任务是管理“对象图”(Object Graph),你需要确保除了你实际需要的对象之外,内存中没有其它的对象。 Objective-C提供了两种内存管理方式: ...…

继续阅读