观察者模式(有时又被称为发布-订阅模式)

在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。

这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

比如我们订阅杂志, 会有一个订阅服务中心, 他负责管理期刊号, 添加用户 和 发送期刊

这里订阅服务中, 期刊, 用户 我们看做3个因素:

用户要订阅, 需要遵循一定的订阅规范(协议)

期刊要能记录有哪些订阅用户

订阅服务中心负责管理, 当有某一期刊更新时, 通知该期刊的订阅用户或者发送新期刊给订阅用户

下面我们依照这个思路构造工程

这里把订阅服务中心看做一个对象, 并把它设计成一个单例 因为一般只会有一个订阅服务中心管理所有的期刊和用户

订阅服务中心对象有以下功能:

添加/删除期刊, 给某一期刊添加/删除订阅用户, 检查期刊号是否存在, 当有更新时通知订阅用户

期刊管理订阅用户信息时, 不能持有订阅用户对象造成内存泄露, 所以用NSHashTable来保存用户信息

用户要遵守一个订阅规范(协议)

SubscriptionCustomerProtocol.h

 #import <Foundation/Foundation.h>

 @protocol SubscriptionCustomerProtocol <NSObject>

 @required
- (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber; @end

下面构造订阅服务中心对象-用单例模式

SubscriptionServiceCenter.h

 #import <UIKit/UIKit.h>
#import "SubscriptionCustomerProtocol.h" @interface SubscriptionServiceCenter : NSObject /**
初始化单例方法 @return 返回单例对象
*/
+ (instancetype)shareInstance; /**
alloc初始化方法 @param zone 地址空间
@return 返回单例对象
*/
+ (id)allocWithZone:(struct _NSZone *)zone; /**
copy方法 @param zone 地址空间
@return 返回单例对象
*/
- (id)copWithZone:(struct _NSZone *)zone; #pragma mark - 维护订阅信息
/**
创建订阅号 @param subscriptionNumber 订阅号码
*/
- (void)createSubscriptionNumber:(NSString *)subscriptionNumber; /**
删除订阅号 @param subscriptionNumber 订阅号码
*/
- (void)removeSubscriptionNUmber:(NSString *)subscriptionNumber; #pragma mark - 维护客户信息
/**
添加客户到具体的订阅号中 @param customer 客户
@param subscriptionNumber 订阅号码
*/
- (void)addCustomer:(id <SubscriptionCustomerProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber; /**
从具体订阅号中移除客户 @param customer 客户
@param subscriptionNumber 订阅号码
*/
- (void)removeCustomer:(id <SubscriptionCustomerProtocol>)customer withSubcriptionNumber:(NSString *)subscriptionNumber; /**
发送消息到具体的订阅号中 @param message 消息
@param subscriptionNumber 订阅号码
*/
- (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber; /**
获取用户列表 @param subscriptionNumber 订阅号码
@return 返回用户列表
*/
- (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber; @end

SubscriptionServiceCenter.m

 #import "SubscriptionServiceCenter.h"

 static NSMutableDictionary *_subscriptionDictionary = nil;

 @implementation SubscriptionServiceCenter

 static SubscriptionServiceCenter *_instance = nil;

 + (instancetype)shareInstance {

     static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_subscriptionDictionary = [NSMutableDictionary dictionary];
_instance = [[super allocWithZone:NULL] init];
}); return _instance;
} + (id)allocWithZone:(struct _NSZone *)zone { return [SubscriptionServiceCenter shareInstance];
} - (id)copWithZone:(struct _NSZone *)zone { return [SubscriptionServiceCenter shareInstance];
} - (void)createSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable == nil) { hashTable = [NSHashTable weakObjectsHashTable];
[_subscriptionDictionary setObject:hashTable forKey:subscriptionNumber];
}
} - (void)removeSubscriptionNUmber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable) { [_subscriptionDictionary removeObjectForKey:subscriptionNumber];
}
} - (void)addCustomer:(id <SubscriptionCustomerProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(customer);
NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
[hashTable addObject:customer];
} - (void)removeCustomer:(id <SubscriptionCustomerProtocol>)customer withSubcriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
[hashTable removeObject:customer];
} - (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber { NSParameterAssert(subscriptionNumber); NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable) { NSEnumerator *enumerator = [hashTable objectEnumerator];
id <SubscriptionCustomerProtocol> object = nil;
while (object = [enumerator nextObject]) { if ([object respondsToSelector:@selector(subscriptionMessage: subscriptionNumber:)]) { [object subscriptionMessage:message subscriptionNumber:subscriptionNumber];
}
}
}
} - (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber { return [_subscriptionDictionary objectForKey:subscriptionNumber];
} @end

下面在Controller中实现, Controller作为用户即观察者

 #import "ViewController.h"
#import "SubscriptionCustomerProtocol.h"
#import "SubscriptionServiceCenter.h" static NSString * SCIENCE = @"SCIENCE"; @interface ViewController () <SubscriptionCustomerProtocol> @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; //创建一个订阅服务中心单例
SubscriptionServiceCenter *center = [SubscriptionServiceCenter shareInstance]; //创建一个订阅号
[center createSubscriptionNumber:SCIENCE]; //添加一个用户
[center addCustomer:self withSubscriptionNumber:SCIENCE]; //发送一个通知消息
[center sendMessage:@"有新的期刊啦" toSubscriptionNumber:SCIENCE]; } #pragma mark - SubscriptionCustomerProtocol
- (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber { NSLog(@"期刊号: %@ 收到消息: %@", subscriptionNumber, message);
} @end

Cocoa touch中的KVO和NSNotificationCenter的原理是观察模式的很好实现, 下面用代码分别演示下用法

KVO的用法

 - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. self.model = [Model new]; //添加KVO
[self.model addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew
context:nil]; //发送信息, 通过修改属性
self.model.name = @"v1.0"; } #pragma mark - KVO方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@", change);
} - (void)dealloc { //移除KVO
[self.model removeObserver:self
forKeyPath:@"name"];
}

NSNotificationCenter的用法

 - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. //添加
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notificationCenterEvent:)
name:@"SCIENCE"
object:nil]; //发送信息
[[NSNotificationCenter defaultCenter] postNotificationName:@"SCIENCE"
object:@"v1.0"]; } #pragma mark - 通知中心方法
- (void)notificationCenterEvent:(id)sender {
NSLog(@"%@", sender);
} - (void)dealloc {
//移除通知中心
[[NSNotificationCenter defaultCenter] removeObserver:self
forKeyPath:@"SCIENCE"]; }

Objective-C 观察者模式--简单介绍和使用的更多相关文章

  1. Reactive ExtensionsLINQ和Rx简单介绍

    LINQ和Rx简单介绍 相信大家都用过Language Integrated Query (LINQ),他是一种强大的工具能够从集合中提取数据.Reactive Extensions(Rx)是对LIN ...

  2. app 下载更新 file-downloader 文件下载库的简单介绍和使用

    app 下载更新 file-downloader 文件下载库的简单介绍和使用 今天介绍一个下载库:file-downloader 文件下载库 说明: * 本文内容来自原 file-downloader ...

  3. Java EE设计模式(主要简单介绍工厂模式,适配器模式和模板方法模式)

    Java EE设计模式分为三种类型,共23种: 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式. 结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式 ...

  4. 嵌入式Linux下MP4视频录制库MP4V2移植和简单介绍

    **************************************************************************************************** ...

  5. Linux配置zookeeper 和zookeeper简单介绍

    一.zookeeper介绍? 一.zookeeper 简单介绍? 1.什么是集群? // 很多台服务器保持连接通讯状态,并且所有的服务器做同一件事就称之为集群 2.什么是zookeeper? 注册中心 ...

  6. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  7. 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍

    一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...

  8. 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍

    一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...

  9. yii2的权限管理系统RBAC简单介绍

    这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...

随机推荐

  1. ffmpeg 内存读写相关

    需要的解码的视频数据在一段内存中.例如,通过其他系统送来的视频数据.同样,有的时候编码后的视频数据也未必要保存成一个文件.例如,要求将编码后的视频数据送给其他的系统进行下一步的处理.以上两种情况就要求 ...

  2. Linux学习笔记(16)-多线程

    什么是多线程?或者说,什么是线程? 按照书本上的描述,所谓线程,便是包含在进程内部的顺序执行流,是进程中实际运作的单位,也是操作系统能够进行调度的最小单位. 一个进程中可以并发多条线程,每条线程并行执 ...

  3. Future和Promise

    Future用于获取异步操作的结果,而Promise则比较抽象,无法直接猜测出其功能. Future Future最早来源于JDK的java.util.concurrent.Future,它用于代表异 ...

  4. ListFragment源码 (待分析)

    /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Versi ...

  5. 如何解决插入Oracle数据中文为乱码问题

    1.首先,Oracle查询编码:select * from v$nls_parameters;//看看是否GBK 2.如果是用Servlet或者别的,插入数据之前输出一下,看看是否乱码.比如: doP ...

  6. js实现下拉菜单

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  7. 【虚拟机】在VMware中安装Server2008之后配置网络连接的几种方式

    VMware虚拟机的网络连接方式分为三种:桥接模式.NAT模式.仅主机(Host Only) (1)桥接模式 桥接模式即在虚拟机中虚拟一块网卡,这样主机和虚拟机在一个网段中就被看作是两个独立的IP地址 ...

  8. 【JBOSS】控制台数据库连接信息

    数据库连接 信息 进入         在这个页面中(IP和端口已自己的为主):ConnectionCount 这个项目代表在服务开启后,总共使用的连接数!ConnectionCreatedCount ...

  9. 分布式系统:怎么简单地解释Paxos算法?从二段提交、三段提交讲到Paxos

    原文:https://www.quora.com/Distributed-Systems/What-is-a-simple-explanation-of-the-Paxos-algorithm 作者: ...

  10. 算法_bitmap算法

    概述 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素.由于采用bit为单位来存储数据,因此在可以大大的节省存储空间     算法思想 32位机器上,一个整形,比 ...