美文网首页
iOS 观察者模式和发布订阅模式

iOS 观察者模式和发布订阅模式

作者: 不知蜕变的挣扎 | 来源:发表于2018-06-26 11:32 被阅读54次

一、观察者模式和发布订阅模式简介

1.1 观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

image

1.2 发布订阅模式

发布订阅模式理念和观察者模式相同,但是处理方式上不同:订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。

image

1.3 两者之间的差异

1、在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

2、发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

二、观察者模式和发布订阅模式优缺点

2.1 优点

共同有点:

1、都可以一对多

2、程序便于扩展

观察者模式:

单向解耦,发布者不需要清楚订阅者何时何地订阅,只需要维护订阅队列,发送消息即可

发布订阅模式:

双向解耦,发布者和订阅者都不用清楚对方,全部由订阅中心做处理

2.2 缺点

1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

4、发布者不知道订阅者是否收到发布的消息。

5、订阅者不知道自己是否收到了发布者发出的所有消息。

6、发送者不能获知订阅者的执行情况。

7、没人知道订阅者何时开始收到消息。

8、发布订阅模式,中心任务过重,一旦崩溃,所有订阅者都会受到影响。

三、应用场景

1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

3、一个对象必须通知其他对象,而并不知道这些对象是谁。

4、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者/订阅发布创建一种链式触发机制。

四、实现

4.1 观察者模式

目标:

 // 将观察者添加到数组中
- (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;
}

观察者:

// 接收到消息通知
- (void)update {
    
    NSLog(@"我观察的目标发生了变化,我接收到了新的信息,%@",self.name);
}

4.2 发布订阅模式
目标:

// 发送消息给通知中心
- (void)change {
    
    [[NotifyCenter shared] notify:@"change"];
}

通知中心:

//创建通知中心单例
+ (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
            }
        }];
    }
}

观察者:

// 接收到消息通知
- (void)update {
    
    NSLog(@"我观察的目标发生了变化,我接收到了新的信息,%@",self.name);
}

4.4 调用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //观察者模式
    
    //创建目标
    self.subject = [[Subject alloc] init];

    //创建观察者1
    Observer *one = [[Observer alloc] init];
    one.name = @"one";

    //向目标订阅通知
    [_subject addObserver:one selector:@selector(update)];

    //创建观察者2
    Observer *two = [[Observer alloc] init];
    two.name = @"two";

    //向目标订阅通知
    [_subject addObserver:two selector:@selector(update)];
        



    
    //发布订阅模式

    //创建目标
    self.subscribe = [[Subscribe alloc] init];
    
    //创建观察者3
    Observer *third = [[Observer alloc] init];
    third.name = @"third";
    
    //观察者向通知中心订阅消息
    [[NotifyCenter shared] addObserver:third selector:@selector(update) name:@"change"];
    
    //创建观察者4
    Observer *four = [[Observer alloc] init];
    four.name = @"four";

    //观察者向通知中心订阅消息
    [[NotifyCenter shared] addObserver:four selector:@selector(update) name:@"change"];

    //按钮触发目标发送消息
    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];

}

- (void)testButton {
    
    NSLog(@"差不多11:45分了,到点吃饭了");
    NSLog(@"观察者模式发送消息");
    [_subject notify];
    
    NSLog(@"发布-订阅模式发送消息");
    [_subscribe change];
}
image

五、总结

1、虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。

2、两种模式都可以用于松散耦合,改进代码管理和潜在的复用。

相关文章

网友评论

      本文标题:iOS 观察者模式和发布订阅模式

      本文链接:https://www.haomeiwen.com/subject/otvpyftx.html