iOS之解耦機(jī)制

聊聊delegate, block, notification, KVO

高內(nèi)聚低耦合是軟件設(shè)計(jì)永恒的主題之一。耦合高的代碼在完成初始階段并不一定顯現(xiàn)出什么問題,甚至看起來比低耦合版的代碼更為簡潔直接,開發(fā)起來也更為快捷。但是,隨著版本迭代,低耦合設(shè)計(jì)一開始開發(fā)快捷的副作用會(huì)逐漸反噬,模塊的維護(hù)和添加新功能會(huì)越來越困難,越來越難以調(diào)試,直到有一天,只剩兩個(gè)選擇,要么重構(gòu),要么死...然而實(shí)際生活中重構(gòu)并不一定給到時(shí)間,產(chǎn)品經(jīng)理也不一定會(huì)理解...

今天討論iOS框架中用于解耦的四大機(jī)制

  • delegate 代理
  • block
  • notification 通知
  • KVO 鍵-值觀察

delegate與block通常被放在一起討論,delegate是代理,block則提供了另外的一種技術(shù)手段起到跟delegate相同的作用(當(dāng)然block有更多其他用途)。這兩者屬于委托模式。

notification通知中心,KVO是cocoa框架中的黑科技,屬性鍵-值觀察,這兩個(gè)有更多的共性,經(jīng)常被放在一起討論,屬于觀察者模式。

委托從技術(shù)實(shí)現(xiàn)角度,常常被歸入適配器的設(shè)計(jì)模式。適配器模式是一種實(shí)現(xiàn)方法,不是目的,無論是委托模式,還是觀察者模式,最終目的指向的都是耦合問題。

委托模式簡單地描述:
A與B兩個(gè)類交互,本來A類能獨(dú)立完成的功能,依舊在A中運(yùn)行,但是把這部分功能的實(shí)現(xiàn)寫在B類中,那么既不影響A的運(yùn)行,又可以把這部分代碼從A類中分離出去。下次A與C發(fā)生交互也沒有問題,只不過委托部分在B與C中的實(shí)現(xiàn)不同。A就可以被多處使用,這就是解耦的好處。

所有delegate能完成的任務(wù),block可以完美的替代。
例如,使用delegate方式的類

@class TouchBox;
@protocol TouchBoxDelegate <NSObject>

- (void)touchedBox:(TouchBox *)box;

@end

@interface TouchBox : UIView

@property (nonatomic, weak) id<TouchBoxDelegate> delegate;

@end

@implementation TouchBox

- (void)touchBox {
    if (_delegate && [_delegate respondsToSelector:@selector(touchedBox:)]) {
        [_delegate touchedBox:self];
    }
}

@end

如果使用block可以完全等價(jià)

@interface TouchBox : UIView

@property (nonatomic, copy) void(^touchedBox)(TouchBox *);

@end

@implementation TouchBox

- (void)touchBox {
    if (self.touchedBox) {
        self.touchedBox(self);
    }
}

@end

區(qū)別在于如果實(shí)現(xiàn)了委托的類中,有幾個(gè)TouchBox,block形式可以很好的實(shí)現(xiàn)委托的分離,而delegate形式的實(shí)現(xiàn)則會(huì)聚合在一個(gè)位置。

- (void)createBox0 {
    TouchBox *box0 = [TouchBox new];
    box0.tag = 0;
}
...
- (void)createBox1 {
    TouchBox *box1 = [TouchBox new];
    box1.tag = 1;
}
...
#pragma mark - TouchBoxDelegate
- (void)touchedBox:(TouchBox *)box {
    if (box.tag == 0) {
        ...
    } else if (box.tag == 1) {
        ...
    } else if (box.tag == 2) {
        ...
    } else if (box.tag == 3) {
        ...
    }
}

實(shí)際情況下,這種實(shí)現(xiàn)委托的方法會(huì)變得非常龐大,甚至?xí)U(kuò)張到幾百行。

如果換成block,委托的實(shí)現(xiàn)會(huì)寫在創(chuàng)建的地方。

- (void)createBox0 {
    TouchBox *box0 = [TouchBox new];
    box0.touchedBox = ^(TouchBox *box) {
        ...
    };
}

- (void)createBox1 {
    TouchBox *box1 = [TouchBox new];
    box1.touchedBox  = ^(TouchBox *box) {
        ...       
    };
}

有利有弊,block分散委托的實(shí)現(xiàn),調(diào)試時(shí)可能不如delegate形式集中,而且block使用起來有retain cycle的隱患。如果block中有創(chuàng)建其他控件,而該控件也用block來實(shí)現(xiàn)委托,可能出現(xiàn)block嵌套的情況,可能會(huì)使情況變得復(fù)雜。

block嵌套

- (void)createBox {
    TouchBox *box0 = [TouchBox new];
    box0.touchedBox = ^(TouchBox *box) {
        TouchBox *box1 = [TouchBox new];
        box1.touchedBox = ^(TouchBox *box) {
            TouchBox *box2 = [TouchBox new];
            box2.touchedBox = ^(TouchBox *box) {
                ...
            };
        };
    };
}

委托模式相對(duì)于觀察者模式來說,委托對(duì)象更加“主動(dòng)”地參與了整個(gè)調(diào)用過程,知道被委托對(duì)象是誰,擁有被委托對(duì)象的引用,由于委托的方法更為具體,參數(shù)的傳遞更為方便。

觀察者模式中,對(duì)象則顯得更“被動(dòng)”,淡化了自身的動(dòng)作,疏離了實(shí)現(xiàn)操作的觀察對(duì)象,參數(shù)傳遞也簡化為了非常抽象的單一Object
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject。鍵-值觀察則沒有額外的參數(shù)可言。

通知的發(fā)生過程是,對(duì)象向一個(gè)中心對(duì)象發(fā)出通知,由通知中心像所有觀察者提供通知,而對(duì)象并不知道觀察者的存在。而鍵-值觀察連通知都不用在程序?qū)崿F(xiàn)時(shí)主動(dòng)發(fā)出(iOS的框架運(yùn)行時(shí)被觀察的變量的setter方法會(huì)發(fā)出通知)。兩者的區(qū)別在于notification通知主要從廣義上關(guān)注程序事件,發(fā)出通知的選擇位置自由度更大,而鍵-值觀察綁定于特定對(duì)象屬性的值,是一種在特定方法中發(fā)出通知的方法(setter方法)。

這些方法本身沒有優(yōu)劣之分,但是有“主動(dòng)”與“被動(dòng)”之分,參數(shù)傳遞的難易程度之分,皆是iOS程序中風(fēng)格各異的解耦之利器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容