iOS-觀察者模式

前言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進行調度的。不過無論是觀察者模式仍是發佈訂閱模式,都是爲了一對多時的對象解耦,能夠說發佈訂閱模式是一種特殊的觀察者模式。

相關文章
相關標籤/搜索