zoukankan      html  css  js  c++  java
  • Objective-C 观察者模式--简单介绍和使用

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

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

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

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

    这里订阅服务中, 期刊, 用户 我们看做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 
    37 @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 }
  • 相关阅读:
    C#后台利用正则表达式查找匹配字符
    C# Dictionary 的几种遍历方法
    eval解析JSON中的注意点
    jquery datatables api (转)
    Replication--镜像+复制
    Replication--分区+复制
    Replication--进程无法在“xxxx”上执行“sp_replcmds”
    Replication--无法将事务提升为分布式事务,因为在事务中有活动的保存点
    Replication--使用MSlogreader_history查看日志读起的延迟和事务命令
    Partition--分区拆分和分区合并
  • 原文地址:https://www.cnblogs.com/zhouxihi/p/6033524.html
Copyright © 2011-2022 走看看