文档章节

iOS-观察者模式

麦兜卖鱼丸
 麦兜卖鱼丸
发布于 2016/08/15 15:04
字数 1233
阅读 56
收藏 0

前言

与其说发布订阅是观察者模式的别名,还不如说发布订阅本质上是一种特殊的观察者模式;两种模式都主要是用于解除一个对象与多个对象之间的耦合,即不管有多少个监听者(observer),都不用改变被监听者(subject)的逻辑代码。

使用的场合

当你需要将某一个对象的改变通知所有的对象的,而且对象是什么类型也不确定的的时候,就应该使用观察者模式,改变发生在同一个对象改变中,并在其他需要的地方更新内容。

 

由于上面我说过了,其实发布订阅与观察者模式还是有区别的,下面我们就通过一个例子,说明一下发布订阅与观察者模式的微小区别。

观察者模式

我为了方便就没有去创建observer和subject的基类,而是直接创建了这两个的具体类(主要是方便测试,不想创建那么多文件),subject类中定义了- (void)addObserver:(NSObject *)obj selector:(SEL)aSelector;和- (void)notify;前者是添加观察者,后者是当subject的状态改变后,通知所有观察者的方法;

(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的改变通知);

#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

 

发布订阅模式

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

© 著作权归作者所有

共有 人打赏支持
下一篇: iOS-重力感应
麦兜卖鱼丸
粉丝 12
博文 69
码字总数 69333
作品 0
桂林
iOS工程师
私信 提问
如何判断你是合格的高级iOS开发工程师?

前言 随着移动互联网的高速发展泄洪而来,有意学习移动开发的人越来越多了,竞争也是越来越大,需要学习的东西很多。如何才能在激烈的移动开发者竞争中一枝独秀,成为一名真正合格的高级iOS...

_小迷糊
2018/05/26
0
0
移动开发之设计模式- 代理模式(IOS&Android)

资源 完全参照 代理模式|菜鸟教程但不包括IOS代码 代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有...

FlanneryZJ
2018/12/18
0
0
移动开发之设计模式-工厂模式(IOS&Android)

资源 完全参照 工厂模式|菜鸟教程 ,但不包括IOS代码 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳...

FlanneryZJ
2018/12/17
0
0
移动开发之设计模式- 命令模式(IOS&Android)

资源 完全参照 命令模式|菜鸟教程但不包括IOS代码 命令模式 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用...

FlanneryZJ
2018/12/19
0
0
移动开发之设计模式-抽象工厂模式(IOS&Android)

资源 完全参照 抽象工厂模式|菜鸟教程 ,但不包括IOS代码 抽象工厂模式 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型...

FlanneryZJ
2018/12/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringMVC工作原理

SpringMVC的工作原理图: SpringMVC流程 1、 用户发送请求至前端控制器DispatcherServlet。 2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。 3、 处理器映射器找到具体的处理...

呵呵哒灬
今天
3
0
数据库技术-Mysql主从复制与数据备份

数据库技术-Mysql 主从复制的原理: MySQL中数据复制的基础是二进制日志文件(binary log file)。一台MySQL数据库一旦启用二进制日志后,其作为master,它的数据库中所有操作都会以“事件”...

须臾之余
昨天
13
0
Git远程仓库——GitHub的使用(一)

Git远程仓库——GitHub的使用(一) 一 、 Git远程仓库 由于你的本地仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要一下设置: 步骤一、 创建SSH key 在用户主目录下,看看有没有.ss...

lwenhao
昨天
4
0
SpringBoot 整合

springBoot 整合模板引擎 SpringBoot 整合Mybatis SpringBoot 整合redis SpringBoot 整合定时任务 SpringBoot 整合拦截器...

细节探索者
昨天
1
0
第二个JAVA应用

第二个JAVA应用 方法一:配置文件: # cd /usr/local/tomcat/conf/# vim server.xml</Host> <Host name="www.wangzb.cc" appBase="/data/wwwroot/www.wangzb.cc" //引用所......

wzb88
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部