美文网首页
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