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进行调度的。不过无论是观察者模式仍是发布订阅模式,都是为了一对多时的对象解耦,能够说发布订阅模式是一种特殊的观察者模式。

相关文章
相关标签/搜索