iOS組件化方案的幾種實(shí)現(xiàn)

最近研究了一下項(xiàng)目的組件化,把casabanglimboy的有關(guān)組件化的博客看了一遍,學(xué)到了不少東西,對(duì)目前業(yè)界的組件化方案有了一定的了解。這些高質(zhì)量的博客大致討論了組件化的三種方案:url-blockprotocol-class(和url-controller類(lèi)似)、target-action,以及應(yīng)用這三種組件化方案的時(shí)機(jī)、步驟、利弊等等。

本文主要介紹一下這三種組件化方案的技術(shù)實(shí)現(xiàn)過(guò)程,針對(duì)不同組件化方案具體應(yīng)用過(guò)程中可能出現(xiàn)的問(wèn)題加以介紹,也針對(duì)casa批判蘑菇街的組件化方案加以自己的思考,希望對(duì)需要了解組件化的朋友有一定的幫助。

為什么需要組件化

隨著公司業(yè)務(wù)的不斷發(fā)展,項(xiàng)目的功能越來(lái)越復(fù)雜,各個(gè)業(yè)務(wù)代碼耦合也越來(lái)越多,代碼量也是急劇增加,傳統(tǒng)的MVC或者MVVM架構(gòu)已經(jīng)無(wú)法高效的管理工程代碼,因此需要用一種技術(shù)來(lái)更好地管理工程,而組件化是一種能夠解決代碼耦合的技術(shù)。項(xiàng)目經(jīng)過(guò)組件化的拆分,不僅可以解決代碼耦合的問(wèn)題,還可以增強(qiáng)代碼的復(fù)用性,工程的易管理性等等。

組件化的過(guò)程

之前根據(jù)蘑菇街的組件化方案,limboycasa等人做了深入的討論,并根據(jù)各自的觀點(diǎn)給出了方案實(shí)施的理由以及利弊關(guān)系,然后又有人改進(jìn)了他們的組件化方案,我總結(jié)了一下,大致有三種,下面分別介紹各自的實(shí)現(xiàn)過(guò)程:

方案一、url-block

這是蘑菇街中應(yīng)用的一種頁(yè)面間調(diào)用的方式,通過(guò)在啟動(dòng)時(shí)注冊(cè)組件提供的服務(wù),把調(diào)用組件使用的url和組件提供的服務(wù)block對(duì)應(yīng)起來(lái),保存到內(nèi)存中。在使用組件的服務(wù)時(shí),通過(guò)url找到對(duì)應(yīng)的block,然后獲取服務(wù)。

下圖是url-block的架構(gòu)圖:

圖1

注冊(cè):

[MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) {
    NSNumber *id = routerParameters[@"id"];
    // create view controller with id
    // push view controller
}];

調(diào)用:

[MGJRouter openURL:@"mgj://detail?id=404"]

蘑菇街為了統(tǒng)一iOSAndroid的平臺(tái)差異性,專(zhuān)門(mén)用后臺(tái)來(lái)管理url,然后針對(duì)不同的平臺(tái),生成不同類(lèi)型的文件,來(lái)方便使用。

使用url-block的方案的確可以組建間的解耦,但是還是存在其它明顯的問(wèn)題,比如:

  1. 需要在內(nèi)存中維護(hù)url-block的表,組件多了可能會(huì)有內(nèi)存問(wèn)題
  2. url的參數(shù)傳遞受到限制,只能傳遞常規(guī)的字符串參數(shù),無(wú)法傳遞非常規(guī)參數(shù),如UIImageNSData等類(lèi)型
  3. 沒(méi)有區(qū)分本地調(diào)用和遠(yuǎn)程調(diào)用的情況,尤其是遠(yuǎn)程調(diào)用,會(huì)因?yàn)?code>url參數(shù)受限,導(dǎo)致一些功能受限
  4. 組件本身依賴(lài)了中間件,且分散注冊(cè)使的耦合較多

方案二、protocol-class

針對(duì)方案一的問(wèn)題,蘑菇街又提出了另一種組件化的方案,就是通過(guò)protocol定義服務(wù)接口,組件通過(guò)實(shí)現(xiàn)該接口來(lái)提供接口定義的服務(wù),具體實(shí)現(xiàn)就是把protocolclass做一個(gè)映射,同時(shí)在內(nèi)存中保存一張映射表,使用的時(shí)候,就通過(guò)protocol找到對(duì)應(yīng)的class來(lái)獲取需要的服務(wù)。

下圖是protocol-class的架構(gòu)圖:

圖2

注冊(cè):

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

調(diào)用:

[ModuleManager classForProtocol:ProtocolA]

蘑菇街的這種方案確實(shí)解決了方案一中無(wú)法傳遞非常規(guī)參數(shù)的問(wèn)題,使得組件間的調(diào)用更為方便,但是它依然沒(méi)有解決組件依賴(lài)中間件的問(wèn)題、內(nèi)存中維護(hù)映射表的問(wèn)題、組件的分散調(diào)用的問(wèn)題。設(shè)計(jì)思想和方案一類(lèi)似,都是通過(guò)給組件加了一層wrapper,然后給使用者調(diào)用。

同時(shí),另一種方案是url-controller,這是LDBusMediator的組件化方案,我認(rèn)為和方案二的實(shí)現(xiàn)原理類(lèi)似。它是通過(guò)組件實(shí)現(xiàn)公共協(xié)議的服務(wù),來(lái)對(duì)外提供服務(wù)。具體就是通過(guò)單例來(lái)維護(hù)url-controller的映射關(guān)系表,根據(jù)調(diào)用者的url,以及提供的參數(shù)(字典類(lèi)型,所以參數(shù)類(lèi)型不受約束)來(lái)返回對(duì)應(yīng)的controller來(lái)提供服務(wù);同時(shí),為了增強(qiáng)組件提供服務(wù)的多樣性,又通過(guò)服務(wù)協(xié)議定義了其它的服務(wù)。整體來(lái)看,LDBusMediator解決了蘑菇街的這兩種組件化方案的不足,比如:通過(guò)注冊(cè)封裝件connector而不是block來(lái)降低了內(nèi)存占用;通過(guò)字典傳遞參數(shù),解決了url參數(shù)的限制性。但是,由于使用了connector來(lái)提供服務(wù)而不是組件本身,把connector作為組件的一部分,依然有組件依賴(lài)中間件的問(wèn)題。

下圖是LDBusMediator的組件化架構(gòu)圖:

圖3

方案三、target-action

casa的方案是通過(guò)給組件包裝一層wrapper來(lái)給外界提供服務(wù),然后調(diào)用者通過(guò)依賴(lài)中間件來(lái)使用服務(wù);其中,中間件是通過(guò)runtime來(lái)調(diào)用組件的服務(wù),是真正意義上的解耦,也是該方案最核心的地方。具體實(shí)施過(guò)程是給組件封裝一層target對(duì)象來(lái)對(duì)外提供服務(wù),不會(huì)對(duì)原來(lái)組件造成入侵;然后,通過(guò)實(shí)現(xiàn)中間件的category來(lái)提供服務(wù)給調(diào)用者,這樣使用者只需要依賴(lài)中間件,而組件則不需要依賴(lài)中間件。

下圖是casa的組件化方案架構(gòu)圖:

圖4

以下代碼來(lái)自casa的組件化demo

target

A組件

// TargetA.h

- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params;

CTMediator分類(lèi)

// CTMediator+CTMediatorModuleAActions.h

- (UIViewController *)CTMediator_viewControllerForDetail;

// CTMediator+CTMediatorModuleAActions.m

- (UIViewController *)CTMediator_viewControllerForDetail
{
    return [self performTarget:kCTMediatorTargetA action:kCTMediatorActionNativFetchDetailViewController params:@{@"key":@"value"} shouldCacheTarget:NO];
}

調(diào)用

// ViewController.h
#import "CTMediator+CTMediatorModuleAActions.h"

[self presentViewController:[[CTMediator sharedInstance] CTMediator_viewControllerForDetail] animated:YES completion:nil];

從以上代碼可以看出,使用者只需要依賴(lài)中間件,而中間件又不依賴(lài)組件,這是真正意義上的解耦。但是casa的這個(gè)方案有個(gè)問(wèn)題就是hardcode,在中間件的category里有hardcodecasa的解釋是在組件間調(diào)用時(shí),最好是去model化,所以不可避免的引入了hardcode,并且所有的hardcode只存在于分類(lèi)中。針對(duì)這個(gè)問(wèn)題,有人提議,把所有的model做成組件化下沉,然后讓所有的組件都可以自由的訪(fǎng)問(wèn)model,不過(guò)在我看來(lái),這種方案雖然解決了組件間傳遞model的依賴(lài)問(wèn)題,但是為了解決這個(gè)小問(wèn)題,直接把整個(gè)model層組件化后暴露給所有組件,容易造成數(shù)據(jù)泄露,付出的代價(jià)有點(diǎn)大。針對(duì)這個(gè)問(wèn)題,經(jīng)過(guò)和網(wǎng)友討論,一致覺(jué)得組件間調(diào)用時(shí)用字典傳遞數(shù)據(jù),組件內(nèi)調(diào)用時(shí)用model傳遞數(shù)據(jù),這樣即減少組件間數(shù)據(jù)對(duì)model的耦合,又方便了組件內(nèi)使用model傳遞數(shù)據(jù)的便捷性。

組件化實(shí)施的方式

組件化可以利用git的源代碼管理工具的便利性來(lái)實(shí)施,具體就是建立一個(gè)項(xiàng)目工程的私有化倉(cāng)庫(kù),然后把各個(gè)組件的podspec上傳到私有倉(cāng)庫(kù),在需要用到組件時(shí),直接從倉(cāng)庫(kù)里面取。

1.封裝公共庫(kù)和基礎(chǔ)UI庫(kù)

在具體的項(xiàng)目開(kāi)發(fā)過(guò)程中,我們常會(huì)用到三方庫(kù)和自己封裝的UI庫(kù),我們可以把這些庫(kù)封裝成組件,然后在項(xiàng)目里用pod進(jìn)行管理。其中,針對(duì)三方庫(kù),最好再封裝一層,使我們的項(xiàng)目部直接依賴(lài)三方庫(kù),方便后續(xù)開(kāi)發(fā)過(guò)程中的更換。

2.獨(dú)立業(yè)務(wù)模塊化

在開(kāi)發(fā)過(guò)程中,對(duì)一些獨(dú)立的模塊,如:登錄模塊、賬戶(hù)模塊等等,也可以封裝成組件,因?yàn)檫@些組件是項(xiàng)目強(qiáng)依賴(lài)的,調(diào)用的頻次比較多。另外,在拆分組件化的過(guò)程中,拆分的粒度要合適,盡量做到組件的獨(dú)立性。同時(shí),組件化是一個(gè)漸進(jìn)的過(guò)程,不可能把一個(gè)完整的工程一下子全部組件化,要分步進(jìn)行,通過(guò)不停的迭代,來(lái)最終實(shí)現(xiàn)項(xiàng)目的組件化。

3.服務(wù)接口最小化

在前兩步都完成的情況下,我們可以根據(jù)組件被調(diào)用的需求來(lái)抽象出組件對(duì)外的最小化接口。這時(shí),就可以選擇具體應(yīng)用哪種組件化方案來(lái)實(shí)施組件化了。

總結(jié)

組件化是項(xiàng)目架構(gòu)層面的技術(shù),不是所有項(xiàng)目都適合組件化,組件化一般針對(duì)的是大中型的項(xiàng)目,并且是多人開(kāi)發(fā)。如果,項(xiàng)目比較小,開(kāi)發(fā)人員比較少,確實(shí)不太適合組件化,因?yàn)檫@時(shí)的組件化可能帶來(lái)的不是便捷,而是增加了開(kāi)發(fā)的工作量。另外,組件化過(guò)程也要考慮團(tuán)隊(duì)的情況,總之,根據(jù)目前項(xiàng)目的情況作出最合適的技術(shù)選型。我一直尊崇,沒(méi)有最好的技術(shù),只有最合適的技術(shù)。


實(shí)際上我的組件化經(jīng)驗(yàn)也不是很多,本文也是根據(jù)casalimboybang等的博客中的內(nèi)容,做了簡(jiǎn)要的分析,針對(duì)文中的不足之處,還望大家指出,共同進(jìn)步。(文中圖片來(lái)自互聯(lián)網(wǎng),版權(quán)歸原作者所有)

參考資料

iOS應(yīng)用架構(gòu)談 組件化方案

iOS組件化實(shí)踐方案-LDBusMediator煉就

iOS組件化思路-大神博客研讀和思考

iOS 組件化方案探索

蘑菇街 App 的組件化之路

蘑菇街 App 的組件化之路·續(xù)

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

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