前言ide
與其說發佈訂閱是觀察者模式的別名,還不如說發佈訂閱本質上是一種特殊的觀察者模式;兩種模式都主要是用於解除一個對象與多個對象之間的耦合,即無論有多少個監聽者(observer),都不用改變被監聽者(subject)的邏輯代碼。測試
使用的場合atom
當你須要將某一個對象的改變通知全部的對象的,並且對象是什麼類型也不肯定的的時候,就應該使用觀察者模式,改變發生在同一個對象改變中,並在其餘須要的地方更新內容。spa
因爲上面我說過了,其實發布訂閱與觀察者模式仍是有區別的,下面咱們就經過一個例子,說明一下發布訂閱與觀察者模式的微小區別。code
觀察者模式orm
我爲了方便就沒有去建立observer和subject的基類,而是直接建立了這兩個的具體類(主要是方便測試,不想建立那麼多文件),subject類中定義了- (void)addObserver:(NSObject *)obj selector:(SEL)aSelector;和- (void)notify;前者是添加觀察者,後者是當subject的狀態改變後,通知全部觀察者的方法;server
(1)subject類;對象
#import <Foundation/Foundation.h> @interface Subject : NSObject - (void)addObserver:(NSObject *)obj selector:(SEL)aSelector; - (void)notify; @end #import "Subject.h" @interface Subject () @property (nonatomic, strong) NSMutableArray *observers; @end @implementation Subject - (void)addObserver:(NSObject *)obj selector:(SEL)aSelector { NSDictionary *dict = @{@"object":obj,@"sel":NSStringFromSelector(aSelector)}; [self.observers addObject:dict]; } - (void)notify { if (_observers) { for (NSDictionary *dict in _observers) { if ([dict isKindOfClass:[NSDictionary class]]) { NSObject *observer = dict[@"object"]; SEL aSelector = NSSelectorFromString(dict[@"sel"]); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [observer performSelector:aSelector]; #pragma clang diagnostic pop } } } } - (NSMutableArray *)observers { if (!_observers) { _observers = [NSMutableArray array]; } return _observers; } @end
(2)observer類,類中定義了name屬性,以及一個update方法(響應subject的改變通知);get
#import <Foundation/Foundation.h> @interface Oberver : NSObject @property (nonatomic,strong) NSString *name; - (void)update; @end #import "Oberver.h" @implementation Oberver - (void)update { NSLog(@"我觀察的目標發生了變化,我接收到了新的信息,%@",_name); } @end
發佈訂閱模式it
observer類我仍是沿用上面的類,新增NotifyCenter和Subscribe類;NotifyCenter中主要的方法有- (void)addObserver:(NSObject *)object selector:(SEL)aSelector name:(NSString *)aName;和- (void)notify:(NSString *)name;前者添加觀察者對象,後者通知全部的觀察者對象發生改變。說到這裏確實看不出兩種模式有什麼區別;別急,再看Subscribe類,裏面很簡單,就一個- (void)change方法;
(1)NotifyCenter類;
#import <Foundation/Foundation.h> @class NotifyCenter; @interface NotifyCenter : NSObject + (NotifyCenter *)shared; - (void)addObserver:(NSObject *)object selector:(SEL)aSelector name:(NSString *)aName; - (void)notify:(NSString *)name; @end #import "NotifyCenter.h" @interface NotifyCenter () @property (nonatomic, strong) NSMutableSet *set; @property (nonatomic, strong) NSMutableArray *observers; @end @implementation NotifyCenter + (NotifyCenter *)shared { static NotifyCenter *shared; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [[self alloc] init]; }); return shared; } - (void)addObserver:(NSObject *)object selector:(SEL)aSelector name:(NSString *)aName { [self.set addObject:aName]; NSDictionary *dict = @{@"object":object,@"sel":NSStringFromSelector(aSelector)}; NSMutableDictionary *observer = [NSMutableDictionary dictionary]; observer[aName] = dict; [self.observers addObject:observer]; } - (void)notify:(NSString *)name { if ([_set containsObject:name]) { [_observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSDictionary *dict = obj[name]; if ([dict isKindOfClass:[NSDictionary class]]) { NSObject *observer = dict[@"object"]; SEL aSelector = NSSelectorFromString(dict[@"sel"]); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [observer performSelector:aSelector]; #pragma clang diagnostic pop } }]; } } - (NSMutableArray *)observers { if (!_observers) { _observers = [NSMutableArray array]; } return _observers; } - (NSMutableSet *)set { if (!_set) { _set = [NSMutableSet set]; } return _set; } @end
(2)Subscribe類;
#import <Foundation/Foundation.h> @interface Subscribe : NSObject - (void)change; @end #import "Subscribe.h" #import "NotifyCenter.h" @implementation Subscribe - (void)change { [[NotifyCenter shared] notify:@"change"]; } @end
下面咱們看看兩個模式的使用,其實邏輯思路都是差很少的,發佈訂閱模式就比如將觀察者模式的subject的邏輯,分爲了發佈訂閱模式中的NotifyCenter和Subscribe,它每一個部分更加專一的完成各自的業務邏輯。
#import "ViewController.h" #import "Subject.h" #import "Oberver.h" #import "NotifyCenter.h" #import "Subscribe.h" @interface ViewController () @property (nonatomic, strong) Subject *subject; @property (nonatomic, strong) Subscribe *subscribe; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //觀察者模式 self.subject = [[Subject alloc] init]; Oberver *one = [[Oberver alloc] init]; one.name = @"one"; [_subject addObserver:one selector:@selector(update)]; Oberver *two = [[Oberver alloc] init]; two.name = @"two"; [_subject addObserver:two selector:@selector(update)]; UIButton *testBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 40)]; testBtn.backgroundColor = [UIColor lightGrayColor]; [testBtn addTarget:self action:@selector(testButton) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:testBtn]; //發佈訂閱模式 self.subscribe = [[Subscribe alloc] init]; Oberver *third = [[Oberver alloc] init]; third.name = @"third"; [[NotifyCenter shared] addObserver:third selector:@selector(update) name:@"change"]; Oberver *four = [[Oberver alloc] init]; four.name = @"four"; [[NotifyCenter shared] addObserver:four selector:@selector(update) name:@"change"]; } - (void)testButton { NSLog(@"差很少11:45分了,到點吃飯了"); NSLog(@"觀察者模式"); [_subject notify]; NSLog(@"發佈-訂閱模式"); [_subscribe change]; } @end
點擊按鈕,獲得運行結果,結果很明瞭;
總結
關於這兩種模式,其實大致上是相同的,而不一樣的地方在於觀察者模式調度的地方在subject類中,而發佈訂閱對訂閱者的調度是在NotifyCenter調度中心;所以觀察者模式subject和observer是存在依賴的,而發佈訂閱則不會,由於它是經過NotifyCenter對observer進行調度的。不過無論是觀察者模式仍是發佈訂閱模式,都是爲了一對多時的對象解耦,能夠說發佈訂閱模式是一種特殊的觀察者模式。