iOS組件化方案的幾種實現

来源:http://www.cnblogs.com/fishbay/archive/2017/07/21/7216084.html
-Advertisement-
Play Games

最近研究了一下項目的組件化,把`casa`、`bang`、`limboy`的有關組件化的博客看了一遍,學到了不少東西,對目前業界的組件化方案有了一定的瞭解。這些高質量的博客大致討論了組件化的三種方案:`url-block`、`protocol-class`(和`url-controller`類似)、... ...


最近研究了一下項目的組件化,把casabanglimboy的有關組件化的博客看了一遍,學到了不少東西,對目前業界的組件化方案有了一定的瞭解。這些高質量的博客大致討論了組件化的三種方案:url-blockprotocol-class(和url-controller類似)、target-action,以及應用這三種組件化方案的時機、步驟、利弊等等。

本文主要介紹一下這三種組件化方案的技術實現過程,針對不同組件化方案具體應用過程中可能出現的問題加以介紹,也針對casa批判蘑菇街的組件化方案加以自己的思考,希望對需要瞭解組件化的朋友有一定的幫助。

為什麼需要組件化

隨著公司業務的不斷發展,項目的功能越來越複雜,各個業務代碼耦合也越來越多,代碼量也是急劇增加,傳統的MVC或者MVVM架構已經無法高效的管理工程代碼,因此需要用一種技術來更好地管理工程,而組件化是一種能夠解決代碼耦合的技術。項目經過組件化的拆分,不僅可以解決代碼耦合的問題,還可以增強代碼的復用性,工程的易管理性等等。

組件化的過程

之前根據蘑菇街的組件化方案,limboycasa等人做了深入的討論,並根據各自的觀點給出了方案實施的理由以及利弊關係,然後又有人改進了他們的組件化方案,我總結了一下,大致有三種,下麵分別介紹各自的實現過程:

方案一、url-block

這是蘑菇街中應用的一種頁面間調用的方式,通過在啟動時註冊組件提供的服務,把調用組件使用的url和組件提供的服務block對應起來,保存到記憶體中。在使用組件的服務時,通過url找到對應的block,然後獲取服務。

下圖是url-block的架構圖:

圖1

註冊:

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

調用:

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

蘑菇街為了統一iOSAndroid的平臺差異性,專門用後臺來管理url,然後針對不同的平臺,生成不同類型的文件,來方便使用。

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

  1. 需要在記憶體中維護url-block的表,組件多了可能會有記憶體問題
  2. url的參數傳遞受到限制,只能傳遞常規的字元串參數,無法傳遞非常規參數,如UIImageNSData等類型
  3. 沒有區分本地調用和遠程調用的情況,尤其是遠程調用,會因為url參數受限,導致一些功能受限
  4. 組件本身依賴了中間件,且分散註冊使的耦合較多

方案二、protocol-class

針對方案一的問題,蘑菇街又提出了另一種組件化的方案,就是通過protocol定義服務介面,組件通過實現該介面來提供介面定義的服務,具體實現就是把protocolclass做一個映射,同時在記憶體中保存一張映射表,使用的時候,就通過protocol找到對應的class來獲取需要的服務。

下圖是protocol-class的架構圖:

圖2

註冊:

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

調用:

[ModuleManager classForProtocol:ProtocolA]

蘑菇街的這種方案確實解決了方案一中無法傳遞非常規參數的問題,使得組件間的調用更為方便,但是它依然沒有解決組件依賴中間件的問題、記憶體中維護映射表的問題、組件的分散調用的問題。設計思想和方案一類似,都是通過給組件加了一層wrapper,然後給使用者調用。

同時,另一種方案是url-controller,這是LDBusMediator的組件化方案,我認為和方案二的實現原理類似。它是通過組件實現公共協議的服務,來對外提供服務。具體就是通過單例來維護url-controller的映射關係表,根據調用者的url,以及提供的參數(字典類型,所以參數類型不受約束)來返回對應的controller來提供服務;同時,為了增強組件提供服務的多樣性,又通過服務協議定義了其它的服務。整體來看,LDBusMediator解決了蘑菇街的這兩種組件化方案的不足,比如:通過註冊封裝件connector而不是block來降低了記憶體占用;通過字典傳遞參數,解決了url參數的限制性。但是,由於使用了connector來提供服務而不是組件本身,把connector作為組件的一部分,依然有組件依賴中間件的問題。

下圖是LDBusMediator的組件化架構圖:

圖3

方案三、target-action

casa的方案是通過給組件包裝一層wrapper來給外界提供服務,然後調用者通過依賴中間件來使用服務;其中,中間件是通過runtime來調用組件的服務,是真正意義上的解耦,也是該方案最核心的地方。具體實施過程是給組件封裝一層target對象來對外提供服務,不會對原來組件造成入侵;然後,通過實現中間件的category來提供服務給調用者,這樣使用者只需要依賴中間件,而組件則不需要依賴中間件。

下圖是casa的組件化方案架構圖:

圖4

以下代碼來自casa的組件化demo

target

A組件

// TargetA.h

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

CTMediator分類

// CTMediator+CTMediatorModuleAActions.h

- (UIViewController *)CTMediator_viewControllerForDetail;

// CTMediator+CTMediatorModuleAActions.m

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

調用

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

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

從以上代碼可以看出,使用者只需要依賴中間件,而中間件又不依賴組件,這是真正意義上的解耦。但是casa的這個方案有個問題就是hardcode,在中間件的category里有hardcodecasa的解釋是在組件間調用時,最好是去model化,所以不可避免的引入了hardcode,並且所有的hardcode只存在於分類中。針對這個問題,有人提議,把所有的model做成組件化下沉,然後讓所有的組件都可以自由的訪問model,不過在我看來,這種方案雖然解決了組件間傳遞model的依賴問題,但是為瞭解決這個小問題,直接把整個model層組件化後暴露給所有組件,容易造成數據泄露,付出的代價有點大。針對這個問題,經過和網友討論,一致覺得組件間調用時用字典傳遞數據,組件內調用時用model傳遞數據,這樣即減少組件間數據對model的耦合,又方便了組件內使用model傳遞數據的便捷性。

組件化實施的方式

組件化可以利用git的源代碼管理工具的便利性來實施,具體就是建立一個項目工程的私有化倉庫,然後把各個組件的podspec上傳到私有倉庫,在需要用到組件時,直接從倉庫裡面取。

1.封裝公共庫和基礎UI庫

在具體的項目開發過程中,我們常會用到三方庫和自己封裝的UI庫,我們可以把這些庫封裝成組件,然後在項目里用pod進行管理。其中,針對三方庫,最好再封裝一層,使我們的項目部直接依賴三方庫,方便後續開發過程中的更換。

2.獨立業務模塊化

在開發過程中,對一些獨立的模塊,如:登錄模塊、賬戶模塊等等,也可以封裝成組件,因為這些組件是項目強依賴的,調用的頻次比較多。另外,在拆分組件化的過程中,拆分的粒度要合適,儘量做到組件的獨立性。同時,組件化是一個漸進的過程,不可能把一個完整的工程一下子全部組件化,要分步進行,通過不停的迭代,來最終實現項目的組件化。

3.服務介面最小化

在前兩步都完成的情況下,我們可以根據組件被調用的需求來抽象出組件對外的最小化介面。這時,就可以選擇具體應用哪種組件化方案來實施組件化了。

總結

組件化是項目架構層面的技術,不是所有項目都適合組件化,組件化一般針對的是大中型的項目,並且是多人開發。如果,項目比較小,開發人員比較少,確實不太適合組件化,因為這時的組件化可能帶來的不是便捷,而是增加了開發的工作量。另外,組件化過程也要考慮團隊的情況,總之,根據目前項目的情況作出最合適的技術選型。我一直尊崇,沒有最好的技術,只有最合適的技術。


實際上我的組件化經驗也不是很多,本文也是根據casalimboybang等的博客中的內容,做了簡要的分析,針對文中的不足之處,還望大家指出,共同進步。(文中圖片來自互聯網,版權歸原作者所有)

參考資料

iOS應用架構談 組件化方案

iOS組件化實踐方案-LDBusMediator煉就

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

iOS 組件化方案探索

蘑菇街 App 的組件化之路

蘑菇街 App 的組件化之路·續


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 介紹: ARC是自iOS 5之後增加的新特性,完全消除了手動管理記憶體的煩瑣,編譯器會自動在適當的地方插入適當的retain、release、autorelease語句。你不再需要擔心記憶體管理,因為編譯器為你處理了一切 註意:ARC 是編譯器特性,而不是 ios 運行時特性(除了weak指針系統),它 ...
  • 將數據存儲到文件中並讀取數據 1、新建FilePersistenceTest項目,並修改activity_main.xml中的代碼,如下:(只加入了EditText,用於輸入文本內容,不管輸入什麼按下back鍵就丟失,我們要做的是數據被回收之前,將它存儲在文件中) 2、修改MainActivity中 ...
  • 最近遇到了一個問題,就是在`UIWebView`的代理方法里,執行`document.title`的`js`代碼無法獲取網頁標題 ...
  • 今天來介紹一個小工具`ccache`,其可以提高`xcode`的編譯速度。說起緣由,是因為我的蘋果電腦配置比較低,而每次開發調試或測試打包都需要編譯工程,雖然項目工程代碼量不算大,但是編譯的時間還是很長,尤其是修改了頭文件或者`Archive`打包時,看著進度條像蝸牛在爬一樣,心裡都有小動物在奔騰.... ...
  • 在開發`iOS`應用程式的過程中,經常需要根據不同的需求,切換到不同的項目配置,或者打不同的包(測試環境、開發環境、生產環境等等),如果每次都是手動配置,一則比較麻煩,二則容易配置錯,那麼有沒有更好的方案來解決這個問題呢?答案是:有的。我們可以根據不同的需求,創建不同的`target`,在不同需求要... ...
  • Android精選源碼 android實現最簡潔的標簽(label/tag)選擇/展示控制項 懂得智能配色的ImageView,還能給自己設置多彩的陰影哦 NicePhoto-基於 Kotlin 開發的 一款超簡單的圖片瀏覽+設置壁紙... 你的桌面從未如此炫酷(一句代碼搞定) 仿支付寶首頁下拉刷新 ...
  • 在開發`iOS`的客戶端應用時,經常需要從伺服器下載圖片,雖然系統提供了下載工具:NSData、NSURLSession等等方法,但是考慮到圖片下載過程中,需要考慮的因素比較多,比如:非同步下載、圖片緩存、錯誤處理、編碼解碼等,以及實際需要中根據不同網路載入不同畫質的圖片等等需求,因此下載操作不是一個... ...
  • 一,效果圖。 二,代碼。 ViewController.h ViewController.m ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...