iOS 觀察者模式和發(fā)布訂閱模式

一破托、觀察者模式和發(fā)布訂閱模式簡介

1.1 觀察者模式

觀察者模式定義了一種一對多的依賴關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象歧蒋。這個(gè)主題對象在狀態(tài)上發(fā)生變化時(shí)土砂,會通知所有觀察者對象,使它們能夠自動更新自己疏尿。

image

1.2 發(fā)布訂閱模式

發(fā)布訂閱模式理念和觀察者模式相同瘟芝,但是處理方式上不同:訂閱者把自己想訂閱的事件注冊到調(diào)度中心易桃,當(dāng)該事件觸發(fā)時(shí)候褥琐,發(fā)布者發(fā)布該事件到調(diào)度中心(順帶上下文),由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊到調(diào)度中心的處理代碼晤郑。

image

1.3 兩者之間的差異

1敌呈、在觀察者模式中贸宏,觀察者是知道Subject的,Subject一直保持對觀察者進(jìn)行記錄磕洪。然而吭练,在發(fā)布訂閱模式中,發(fā)布者和訂閱者不知道對方的存在析显。它們只有通過消息代理進(jìn)行通信鲫咽。

2、發(fā)布訂閱模式中谷异,組件是松散耦合的分尸,正好和觀察者模式相反。

二歹嘹、觀察者模式和發(fā)布訂閱模式優(yōu)缺點(diǎn)

2.1 優(yōu)點(diǎn)

共同有點(diǎn):

1箩绍、都可以一對多

2、程序便于擴(kuò)展

觀察者模式:

單向解耦尺上,發(fā)布者不需要清楚訂閱者何時(shí)何地訂閱材蛛,只需要維護(hù)訂閱隊(duì)列,發(fā)送消息即可

發(fā)布訂閱模式:

雙向解耦怎抛,發(fā)布者和訂閱者都不用清楚對方卑吭,全部由訂閱中心做處理

2.2 缺點(diǎn)

1、如果一個(gè)被觀察者對象有很多的直接和間接的觀察者的話抽诉,將所有的觀察者都通知到會花費(fèi)很多時(shí)間陨簇。

2、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話迹淌,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用河绽,可能導(dǎo)致系統(tǒng)崩潰。 3唉窃、沒有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的耙饰,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。

4纹份、發(fā)布者不知道訂閱者是否收到發(fā)布的消息苟跪。

5、訂閱者不知道自己是否收到了發(fā)布者發(fā)出的所有消息蔓涧。

6件已、發(fā)送者不能獲知訂閱者的執(zhí)行情況。

7元暴、沒人知道訂閱者何時(shí)開始收到消息篷扩。

8、發(fā)布訂閱模式茉盏,中心任務(wù)過重鉴未,一旦崩潰枢冤,所有訂閱者都會受到影響。

三铜秆、應(yīng)用場景

1淹真、一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一個(gè)方面连茧。將這些方面封裝在獨(dú)立的對象中使它們可以各自獨(dú)立地改變和復(fù)用核蘸。

2、一個(gè)對象的改變將導(dǎo)致其他一個(gè)或多個(gè)對象也發(fā)生改變啸驯,而不知道具體有多少對象將發(fā)生改變值纱,可以降低對象之間的耦合度。

3坯汤、一個(gè)對象必須通知其他對象虐唠,而并不知道這些對象是誰。

4惰聂、需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈疆偿,A對象的行為將影響B(tài)對象,B對象的行為將影響C對象……搓幌,可以使用觀察者/訂閱發(fā)布創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制杆故。

四、實(shí)現(xiàn)

4.1 觀察者模式

目標(biāo):

 // 將觀察者添加到數(shù)組中
- (void)addObserver:(NSObject *)obj selector:(SEL)aSelector {
    
    NSDictionary *dict = @{@"object":obj,@"sel":NSStringFromSelector(aSelector)};
    [self.observers addObject:dict];
}
//發(fā)送通知給觀察者
- (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
            }

            
        }
    }
}
//創(chuàng)建觀察者數(shù)組
- (NSMutableArray *)observers {
    if (!_observers) {
        _observers = [NSMutableArray array];
    }
    
    return _observers;
}

觀察者:

// 接收到消息通知
- (void)update {
    
    NSLog(@"我觀察的目標(biāo)發(fā)生了變化溉愁,我接收到了新的信息,%@",self.name);
}

4.2 發(fā)布訂閱模式
目標(biāo):

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

通知中心:

//創(chuàng)建通知中心單例
+ (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(@"我觀察的目標(biāo)發(fā)生了變化处铛,我接收到了新的信息,%@",self.name);
}

4.4 調(diào)用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //觀察者模式
    
    //創(chuàng)建目標(biāo)
    self.subject = [[Subject alloc] init];

    //創(chuàng)建觀察者1
    Observer *one = [[Observer alloc] init];
    one.name = @"one";

    //向目標(biāo)訂閱通知
    [_subject addObserver:one selector:@selector(update)];

    //創(chuàng)建觀察者2
    Observer *two = [[Observer alloc] init];
    two.name = @"two";

    //向目標(biāo)訂閱通知
    [_subject addObserver:two selector:@selector(update)];
        



    
    //發(fā)布訂閱模式

    //創(chuàng)建目標(biāo)
    self.subscribe = [[Subscribe alloc] init];
    
    //創(chuàng)建觀察者3
    Observer *third = [[Observer alloc] init];
    third.name = @"third";
    
    //觀察者向通知中心訂閱消息
    [[NotifyCenter shared] addObserver:third selector:@selector(update) name:@"change"];
    
    //創(chuàng)建觀察者4
    Observer *four = [[Observer alloc] init];
    four.name = @"four";

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

    //按鈕觸發(fā)目標(biāo)發(fā)送消息
    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分了,到點(diǎn)吃飯了");
    NSLog(@"觀察者模式發(fā)送消息");
    [_subject notify];
    
    NSLog(@"發(fā)布-訂閱模式發(fā)送消息");
    [_subscribe change];
}
image

五拐揭、總結(jié)

1撤蟆、雖然兩種模式都存在訂閱者和發(fā)布者(具體觀察者可認(rèn)為是訂閱者、具體目標(biāo)可認(rèn)為是發(fā)布者)堂污,但是觀察者模式是由具體目標(biāo)調(diào)度的家肯,而發(fā)布/訂閱模式是統(tǒng)一由調(diào)度中心調(diào)的,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的盟猖,而發(fā)布/訂閱模式則不會讨衣。

2、兩種模式都可以用于松散耦合式镐,改進(jìn)代碼管理和潛在的復(fù)用反镇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市娘汞,隨后出現(xiàn)的幾起案子歹茶,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辆亏,死亡現(xiàn)場離奇詭異,居然都是意外死亡鳖目,警方通過查閱死者的電腦和手機(jī)扮叨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來领迈,“玉大人彻磁,你說我怎么就攤上這事±晖保” “怎么了衷蜓?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尘喝。 經(jīng)常有香客問我磁浇,道長,這世上最難降的妖魔是什么朽褪? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任置吓,我火速辦了婚禮,結(jié)果婚禮上缔赠,老公的妹妹穿的比我還像新娘衍锚。我一直安慰自己,他們只是感情好嗤堰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布戴质。 她就那樣靜靜地躺著,像睡著了一般踢匣。 火紅的嫁衣襯著肌膚如雪告匠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天离唬,我揣著相機(jī)與錄音凫海,去河邊找鬼。 笑死男娄,一個(gè)胖子當(dāng)著我的面吹牛行贪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播模闲,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼建瘫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尸折?” 一聲冷哼從身側(cè)響起啰脚,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后橄浓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粒梦,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年荸实,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匀们。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡准给,死狀恐怖泄朴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情露氮,我是刑警寧澤祖灰,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站畔规,受9級特大地震影響局扶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叁扫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一详民、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧陌兑,春花似錦沈跨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至软驰,卻和暖如春涧窒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锭亏。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工纠吴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慧瘤。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓戴已,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锅减。 傳聞我的和親對象是個(gè)殘疾皇子糖儡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容