观察者模式(有时又被称为发布-订阅模式)
在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。
这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
比如我们订阅杂志, 会有一个订阅服务中心, 他负责管理期刊号, 添加用户 和 发送期刊
这里订阅服务中, 期刊, 用户 我们看做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 }