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

一、觀察者模式和發(fā)布訂閱模式簡(jiǎn)介

1.1 觀察者模式

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

image

1.2 發(fā)布訂閱模式

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

image

1.3 兩者之間的差異

1、在觀察者模式中,觀察者是知道Subject的,Subject一直保持對(duì)觀察者進(jìn)行記錄。然而,在發(fā)布訂閱模式中,發(fā)布者和訂閱者不知道對(duì)方的存在。它們只有通過(guò)消息代理進(jìn)行通信。

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

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

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

共同有點(diǎn):

1、都可以一對(duì)多

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

觀察者模式:

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

發(fā)布訂閱模式:

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

2.2 缺點(diǎn)

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

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

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

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

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

7、沒(méi)人知道訂閱者何時(shí)開始收到消息。

8、發(fā)布訂閱模式,中心任務(wù)過(guò)重,一旦崩潰,所有訂閱者都會(huì)受到影響。

三、應(yīng)用場(chǎng)景

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

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

3、一個(gè)對(duì)象必須通知其他對(duì)象,而并不知道這些對(duì)象是誰(shuí)。

4、需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈,A對(duì)象的行為將影響B(tài)對(duì)象,B對(duì)象的行為將影響C對(duì)象……,可以使用觀察者/訂閱發(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ā)布/訂閱模式則不會(huì)。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評(píng)論 2 372

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