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 }

 

posted @ 2016-11-05 17:12  周希  阅读(979)  评论(0编辑  收藏  举报