iOS組件化方案對比

米米狗· 2019-05-17

背景

隨著公司業務的不斷發展,項目的功能越來越復雜,各個業務代碼耦合也越來越多,代碼量也是急劇增加,傳統的MVC或者MVVM架構已經無法高效的管理工程代碼,因此需要用一種技術來更好地管理工程,而組件化(也可稱為??榛┦且恢幟芄喚餼齟腭詈系募際?。項目經過組件化的拆分,不僅可以解決代碼耦合的問題,還可以增強代碼的復用性,工程的易管理性等等。

市場上的方案:

方案一、url-block

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

下圖是`url-block`的架構圖:

image.png

注冊:

[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"]

優點:

  1. h5外跳到蘑菇街app,app內部的h5和app的原生交互,都可以直接使用這些定義好的路由。

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

缺點:

  1. 需要在內存中維護url-block的表,組件多了可能會有內存問題。

  2. url的參數傳遞受到限制,只能傳遞常規的字符串參數,無法傳遞非常規參數,如UIImage、NSData等類型。

  3. 沒有區分本地調用和遠程調用的情況,尤其是遠程調用,會因為url參數受限,導致一些功能受限。

  4. 組件本身依賴了中間件,且分散注冊使的耦合較多。

  5. url注冊對于實施組件化是完全沒有必要的,查找 URL 的實現不夠高效。

  6. 路由寫錯后編譯沒問題,而實現運行就出問題了,以后維護也不方便。

方案二、protocol-class

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

下圖是protocol-class的架構圖:

image.png

注冊:

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

調用:

[ModuleManager classForProtocol:ProtocolA]

優點:

  1. 蘑菇街的這種方案確實解決了方案一中無法傳遞非常規參數的問題。

  2. 組件間的調用更為方便。

  3. 解耦代碼量少,實現方便,以后維護也方便。

  4. 協議方法改變后,編譯就會報錯,避免代碼修改遺漏。

  5. 協議方法未實現的話,會報編譯警告。

  6. 方法查找容易,調用高效。

缺點:

  1. 組件的方法調用分散。

  2. 內存中維護映射表。

  3. 協議方法有可能未實現。

  4. 對組件協議需要注冊,不注冊就無法調用。

方案三、url-controller

這是LDBusMediator的組件化方案,它是通過組件實現公共協議的服務,來對外提供服務。具體就是通過單例來維護url-controller的映射關系表,根據調用者的url,以及提供的參數(字典類型,所以參數類型不受約束)來返回對應的controller來提供服務;同時,為了增強組件提供服務的多樣性,又通過服務協議定義了其它的服務。

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

image.png


優點:

  1. LDBusMediator解決了蘑菇街的這兩種組件化方案的不足,比如:通過注冊封裝件connector而不是block來降低了內存占用。

  2. 通過字典傳遞參數,解決了url參數的限制性。

缺點:

  1. 內存中維護映射表。

  2. 組件本身依賴了中間件,且分散注冊使的耦合較多。

  3. url注冊對于實施組件化是完全沒有必要的,查找 URL 的實現不夠高效。

  4. 路由寫錯后編譯沒問題,而實現運行就出問題了,以后維護也不方便。

方案四、target-action

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

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

image.png

優點:

  1. 內存中不需要維護映射表。

  2. 不需要注冊。

缺點:

  1. 中間件實現繁瑣。

  2. 中間件方法類型、參數、返回值不夠靈活。

  3. 中間件方法內部使用字符串來調用方法和類,調用死板。

  4. 中間件方法內部容易寫錯,寫錯后編譯沒問題,而實現運行就出問題了,以后維護也不方便。

方案五、Extension和Category結合使用

東方財富浪客直播的不同組件控制器跳轉使用了這種方式,這個方式是我以前想出來的。在基礎組件定義一個UIViewController 的Extension和Category,Extension里面封裝各種組件間控制器跳轉的調用方法,這里面沒有實現,Category里面也和Extension對應封裝相應方法,Category有實現,內部是調用的Extension封裝的方法,調用時先判斷Extension的方法有沒有實現,做下?;?。組件控制器跳轉調用Category封裝的方法。組件負責實現Extension定義的方法。

當然這種方案不只適用于控制器跳轉,還可以改造成NSObject 的Extension和Category,適應所有的組件間通信。

Extension方法定義:

@interface UIViewController (EL)

/** 發直播 **/
- (void)el_startPushLive:(NSString *_Nullable)liveTitle;

Category方法定義:

@interface UIViewController (elbasic)

/** 發起直播 **/
- (void)elbasic_startPushLive:(NSString *_Nullable)liveTitle;

Category方法實現:

@implementation UIViewController (elbasic)
/** 發起直播 **/
- (void)elbasic_startPushLive:(NSString *_Nullable)liveTitle {
   SEL sel = @selector(el_startPushLive:);
   if ([self respondsToSelector:sel]) {
       [self el_startPushLive:liveTitle];
   }
}

Extension方法實現:

@implementation UIViewController ()

//發直播
- (void)el_startPushLive:(NSString *_Nullable)liveTitle {
   UIViewController *pCtrl = [SBURLAction sb_initCtrl:el_actionurl_push_live(liveTitle)];
   [self el_presentPushCtrl:pCtrl];
}

優點:

  1. Category實現內部做了Extension方法是否實現的判斷,避免了不實現出現的崩潰

  2. 確實解決了方案一中無法傳遞非常規參數的問題

  3. 組件間的調用方便。

  4. 協議方法改變后,編譯就會報錯,避免代碼修改遺漏。

  5. 內存中不需要維護映射表。

缺點:

  1. Extension和Category都定義了同樣的方法,相比protocol-class方案不夠簡潔。

  2. 方法為了避免沖突,都要加前綴。

總結:

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

最后我還是推薦下我的組件化解耦方案,GitHub地址是https://github.com/mlcldh/LCLive。

七星彩秘籍预测码 龙虎赌博押注技巧 pk10一天稳赚几百万 3分赛车计划软件 北京pk10哪种最稳 快三单双计划软件下载 有重庆时时彩预测龙虎 赌场限红规则 qq游戏欢乐二人雀神 亚洲女篮中日决赛 重庆时时开奖结果记录官 幸运飞航收费计划软件 重庆时时开彩结果 28全包必赚 河北时时官网下载 彩票可以买大小吗