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

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

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

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

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

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

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

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

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

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

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

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

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

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

SubscriptionCustomerProtocol.h

  1. #import <Foundation/Foundation.h>
  2.  
  3. @protocol SubscriptionCustomerProtocol <NSObject>
  4.  
  5. @required
  6. - (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber;
  7.  
  8. @end

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

SubscriptionServiceCenter.h

  1. #import <UIKit/UIKit.h>
  2. #import "SubscriptionCustomerProtocol.h"
  3.  
  4. @interface SubscriptionServiceCenter : NSObject
  5.  
  6. /**
  7. 初始化单例方法
  8.  
  9. @return 返回单例对象
  10. */
  11. + (instancetype)shareInstance;
  12.  
  13. /**
  14. alloc初始化方法
  15.  
  16. @param zone 地址空间
  17. @return 返回单例对象
  18. */
  19. + (id)allocWithZone:(struct _NSZone *)zone;
  20.  
  21. /**
  22. copy方法
  23.  
  24. @param zone 地址空间
  25. @return 返回单例对象
  26. */
  27. - (id)copWithZone:(struct _NSZone *)zone;
  28.  
  29. #pragma mark - 维护订阅信息
  30. /**
  31. 创建订阅号
  32.  
  33. @param subscriptionNumber 订阅号码
  34. */
  35. - (void)createSubscriptionNumber:(NSString *)subscriptionNumber;
  36.  
  37. /**
  38. 删除订阅号
  39.  
  40. @param subscriptionNumber 订阅号码
  41. */
  42. - (void)removeSubscriptionNUmber:(NSString *)subscriptionNumber;
  43.  
  44. #pragma mark - 维护客户信息
  45. /**
  46. 添加客户到具体的订阅号中
  47.  
  48. @param customer 客户
  49. @param subscriptionNumber 订阅号码
  50. */
  51. - (void)addCustomer:(id <SubscriptionCustomerProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber;
  52.  
  53. /**
  54. 从具体订阅号中移除客户
  55.  
  56. @param customer 客户
  57. @param subscriptionNumber 订阅号码
  58. */
  59. - (void)removeCustomer:(id <SubscriptionCustomerProtocol>)customer withSubcriptionNumber:(NSString *)subscriptionNumber;
  60.  
  61. /**
  62. 发送消息到具体的订阅号中
  63.  
  64. @param message 消息
  65. @param subscriptionNumber 订阅号码
  66. */
  67. - (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber;
  68.  
  69. /**
  70. 获取用户列表
  71.  
  72. @param subscriptionNumber 订阅号码
  73. @return 返回用户列表
  74. */
  75. - (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber;
  76.  
  77. @end

SubscriptionServiceCenter.m

  1. #import "SubscriptionServiceCenter.h"
  2.  
  3. static NSMutableDictionary *_subscriptionDictionary = nil;
  4.  
  5. @implementation SubscriptionServiceCenter
  6.  
  7. static SubscriptionServiceCenter *_instance = nil;
  8.  
  9. + (instancetype)shareInstance {
  10.  
  11. static dispatch_once_t onceToken;
  12. dispatch_once(&onceToken, ^{
  13. _subscriptionDictionary = [NSMutableDictionary dictionary];
  14. _instance = [[super allocWithZone:NULL] init];
  15. });
  16.  
  17. return _instance;
  18. }
  19.  
  20. + (id)allocWithZone:(struct _NSZone *)zone {
  21.  
  22. return [SubscriptionServiceCenter shareInstance];
  23. }
  24.  
  25. - (id)copWithZone:(struct _NSZone *)zone {
  26.  
  27. return [SubscriptionServiceCenter shareInstance];
  28. }
  29.  
  30. - (void)createSubscriptionNumber:(NSString *)subscriptionNumber {
  31.  
  32. NSParameterAssert(subscriptionNumber);
  33.  
  34. NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
  35. if (hashTable == nil) {
  36.  
  37. hashTable = [NSHashTable weakObjectsHashTable];
  38. [_subscriptionDictionary setObject:hashTable forKey:subscriptionNumber];
  39. }
  40. }
  41.  
  42. - (void)removeSubscriptionNUmber:(NSString *)subscriptionNumber {
  43.  
  44. NSParameterAssert(subscriptionNumber);
  45.  
  46. NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
  47. if (hashTable) {
  48.  
  49. [_subscriptionDictionary removeObjectForKey:subscriptionNumber];
  50. }
  51. }
  52.  
  53. - (void)addCustomer:(id <SubscriptionCustomerProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber {
  54.  
  55. NSParameterAssert(customer);
  56. NSParameterAssert(subscriptionNumber);
  57.  
  58. NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
  59. [hashTable addObject:customer];
  60. }
  61.  
  62. - (void)removeCustomer:(id <SubscriptionCustomerProtocol>)customer withSubcriptionNumber:(NSString *)subscriptionNumber {
  63.  
  64. NSParameterAssert(subscriptionNumber);
  65.  
  66. NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
  67. [hashTable removeObject:customer];
  68. }
  69.  
  70. - (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber {
  71.  
  72. NSParameterAssert(subscriptionNumber);
  73.  
  74. NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
  75. if (hashTable) {
  76.  
  77. NSEnumerator *enumerator = [hashTable objectEnumerator];
  78. id <SubscriptionCustomerProtocol> object = nil;
  79. while (object = [enumerator nextObject]) {
  80.  
  81. if ([object respondsToSelector:@selector(subscriptionMessage: subscriptionNumber:)]) {
  82.  
  83. [object subscriptionMessage:message subscriptionNumber:subscriptionNumber];
  84. }
  85. }
  86. }
  87. }
  88.  
  89. - (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber {
  90.  
  91. return [_subscriptionDictionary objectForKey:subscriptionNumber];
  92. }
  93.  
  94. @end

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

  1. #import "ViewController.h"
  2. #import "SubscriptionCustomerProtocol.h"
  3. #import "SubscriptionServiceCenter.h"
  4.  
  5. static NSString * SCIENCE = @"SCIENCE";
  6.  
  7. @interface ViewController () <SubscriptionCustomerProtocol>
  8.  
  9. @end
  10.  
  11. @implementation ViewController
  12.  
  13. - (void)viewDidLoad {
  14. [super viewDidLoad];
  15.  
  16. //创建一个订阅服务中心单例
  17. SubscriptionServiceCenter *center = [SubscriptionServiceCenter shareInstance];
  18.  
  19. //创建一个订阅号
  20. [center createSubscriptionNumber:SCIENCE];
  21.  
  22. //添加一个用户
  23. [center addCustomer:self withSubscriptionNumber:SCIENCE];
  24.  
  25. //发送一个通知消息
  26. [center sendMessage:@"有新的期刊啦" toSubscriptionNumber:SCIENCE];
  27.  
  28. }
  29.  
  30. #pragma mark - SubscriptionCustomerProtocol
  31. - (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber {
  32.  
  33. NSLog(@"期刊号: %@ 收到消息: %@", subscriptionNumber, message);
  34. }
  35.  
  36. @end

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

KVO的用法

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. // Do any additional setup after loading the view, typically from a nib.
  4.  
  5. self.model = [Model new];
  6.  
  7. //添加KVO
  8. [self.model addObserver:self
  9. forKeyPath:@"name"
  10. options:NSKeyValueObservingOptionNew
  11. context:nil];
  12.  
  13. //发送信息, 通过修改属性
  14. self.model.name = @"v1.0";
  15.  
  16. }
  17.  
  18. #pragma mark - KVO方法
  19. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  20. NSLog(@"%@", change);
  21. }
  22.  
  23. - (void)dealloc {
  24.  
  25. //移除KVO
  26. [self.model removeObserver:self
  27. forKeyPath:@"name"];
  28. }

NSNotificationCenter的用法

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. // Do any additional setup after loading the view, typically from a nib.
  4.  
  5. //添加
  6. [[NSNotificationCenter defaultCenter] addObserver:self
  7. selector:@selector(notificationCenterEvent:)
  8. name:@"SCIENCE"
  9. object:nil];
  10.  
  11. //发送信息
  12. [[NSNotificationCenter defaultCenter] postNotificationName:@"SCIENCE"
  13. object:@"v1.0"];
  14.  
  15. }
  16.  
  17. #pragma mark - 通知中心方法
  18. - (void)notificationCenterEvent:(id)sender {
  19. NSLog(@"%@", sender);
  20. }
  21.  
  22. - (void)dealloc {
  23. //移除通知中心
  24. [[NSNotificationCenter defaultCenter] removeObserver:self
  25. forKeyPath:@"SCIENCE"];
  26.  
  27. }

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. Coming

    Hi,there, I am coming here to keep a great habit-one day a blog to share what new I have learned tod ...

  2. Git在window的使用(TortoiseGit)之一

    一.什么是Git? Git是分布式版本控制系统.它与SVN的主要区别:SVN在本地没有版本,不能脱机工作:Git是分布式控制系统,在自己的本地都有一个版本,可以脱机工作. 二.在window上安装Gi ...

  3. 窗体Showmedol 遇到的奇怪异常: cannot make a visible window model

    //窗体Showmedol 遇到的奇怪异常: cannot make a visible window model //背景:ShowModal A窗体,A窗体再ShowModal B窗体:A是透明背 ...

  4. GridView中实现DropDownList联动

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  5. XVII Open Cup named after E.V. Pankratiev. GP of SPb

    A. Array Factory 将下标按前缀和排序,然后双指针,维护最大的右边界即可. #include<cstdio> #include<algorithm> using ...

  6. 【HDU】1599 find the mincost route

    题意 \(n(1 \le n \le 100)\)个点\(m(1 \le m \le 1000)\)条加权边的无负环无向图,求一个最小环. 分析 加入有一个环,其编号最大的点为\(L\),那么这个环可 ...

  7. 我的前端故事----Ajax方式和jsonp的实现区别

    很久没有更新博客了,毕业2个月了,这段时间一直在忙于工作,一直没有时间更新,最近做的活动突然发现之前的经验居然忘记了...索性想想还是重新开始用博客记录平日里的工作经验吧,吐槽就到这里了,这篇记录的是 ...

  8. linux install mysql

    sudo apt-get install mysql-server #此处会输入root的密码,设置的密码要记住 sudo apt-get install mysql-client sudo apt- ...

  9. Ubuntu创建桌面快捷方式

    默认情况下,ubuntu会将自动安装的软件快捷方式保存在/usr/share/applications目录下,如果我们要创建桌面快捷方式,只需要右键-复制-桌面 就Ok,如图: 上面的方法是通过系统自 ...

  10. 关于dev无法更新、调试的问题