iOS元件化解決方案
由於近期迭代週期變長,有時間想想程式碼持續改進的問題,再加上各業務模組程式碼從去年雜亂無章的狀態,到目前整體結構基本清晰,進而想到了模組之間解耦的問題,於是有了本文,關於iOS元件化的一些思路及最終的解決方案。
為什麼要元件化
技術界如今已存在很多關於元件化的解決方案,Class-Protocol、Target-Action等等,無論採用哪種方案,大家的目的都是為了解決程式碼龐大到一定規模時,依舊可以比較方便的進行管理和開發。這個時候就需要對各個業務模組進行梳理,在程式碼層面實現高內聚、低耦合,降低它們相互之間的變化帶來的影響,從而提升開發效率。
先來看看如下兩個圖,對比一下:
模組間跳轉現狀.jpg
中間層框架解耦方案.jpg
從圖中可以看出,在經過中間層框架跳轉分發之後,各業務模組之間不存在引用關係,程式碼相互隔離,呼叫層次清晰,實現了模組間的真正解耦,完美的過渡到元件化的流程。
框架內部的實現原理是什麼
這裡採用的是openURL: 和 openWithMapKey:的兩種呼叫方式,以便實現App之間跳轉及模組之間的跳轉操作,具體採用哪種方式之後會講到,下面來看一下中間層框架 簡單的呼叫過程:
中間層跳轉分發流程.jpg
接下來詳細介紹下流程中出現的方法及相關類:
openURL:
支援App之間的跳轉
支援設定、電話等系統Apps和info.plist白名單中的第三方Apps跳轉支援Module之間的跳轉
傳參時僅支援NSString
資料型別的賦值支援Module內部頁面的跳轉
傳參時僅支援NSString
資料型別的賦值
openWithMapKey:
支援Module內部頁面的跳轉
傳參時支援NSString
、NSArray
、UIImage
等系統資料型別及自定義資料型別的賦值支援Module之間的跳轉
為了模組間的解耦,傳參時建議使用系統資料型別,避免使用自定義資料型別
總結:兩種呼叫方法各有優勢,透過推送、Widget、第三方App等外部入口開啟你的App執行跳轉操作時,建議使用openURL:,其他情況的跳轉都採用openWithMapKey:的呼叫方式。
JCModuleMap
從上圖可以看出,模組間無論是透過 openURL: 還是 openWithMapKey: 方法,都是找到對應的JCModuleMap
,然後實現最終的頁面跳轉。接下來看看如何透過JCModuleMap
實現這一操作:
-
子類化
JCModuleMap子類化.jpg
// JCTestModuleMap.mNSString *const JCFirstLevelMapKey = @"JC_firstLevel";NSString *const JCSecondLevelMapKey = @"JC_secondLevel";NSString *const JCThirdLevelMapKey = @"JC_thirdLevel";NSString *const JCContentDetailMapKey = @"JC_contentDetail";@implementation JCTestModuleMap- (NSString *)mapKeyPrefix { return @"JC"; } - (NSDictionary*)classesForMapKeys { return @{JCFirstLevelMapKey: NSClassFromString(@"JCFirstLevelViewController"), JCSecondLevelMapKey: NSClassFromString(@"JCSecondLevelViewController"), JCThirdLevelMapKey: NSClassFromString(@"JCThirdLevelViewController"), JCContentDetailMapKey: NSClassFromString(@"JCContentDetailViewController"), }; }@end
-
NSURL/mapKey對映原理
NSURL:mapKey對映原理.jpg
總結:openURL: 透過NSURL及子類化JCModuleMap
中實現的mapKeyPrefix拼接對應的mapKey,然後和 openWithMapKey: 一樣,都是透過classesForMapKeys方法,獲取class-mapKey的對映關係,進而跳轉到module中對應class的檢視控制器頁面。
引數傳遞
申明介面協議,以屬性的方式傳遞:
@protocol JC_contentDetail@property (nonatomic, strong) NSString *currentIndex;@property (nonatomic, strong) NSString *testId;@property (nonatomic, strong) NSArray *testArray;@end@interface JCContentDetailViewController : UIViewController @end
透過
屬性名-屬性值
生成字典傳參:
+ (void)openContentDetailViewControllerWithCurrentIndex:(NSString *)currentIndex testId:(NSString *)testId testArray:(NSArray *)testArray { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3]; if ([currentIndex isKindOfClass:[NSString class]]) { params[@"currentIndex"] = currentIndex; } if ([testId isKindOfClass:[NSString class]]) { params[@"testId"] = testId; } if ([testArray isKindOfClass:[NSArray class]]) { params[@"testArray"] = testArray; } [[JCNavigator sharedNavigator] openWithMapKey:JCContentDetailMapKey propertiesBlock:^NSDictionary *{ return params; } presented:YES animated:YES]; }
頁面展示效果設定
openURL: 時,透過
JCModuleMap
子類化實現的方法設定:
// 是否模態彈出(預設NO)- (BOOL)presentedForClass:(Class)viewControllerClass;// 是否有動畫(預設YES)- (BOOL)animatedForClass:(Class)viewControllerClass;
openWithMapKey:方法呼叫時直接設定:
// 是否模態及動畫- (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block presented:(BOOL)presented animated:(BOOL)animated;
Modules之間的解耦是怎麼實現的
好了,在實現JCModuleMap
子類化及相關跳轉配置之後,現在最關鍵的操作來了,如何為各個modules之間提供通訊及呼叫的介面,才能最大限度的解決解耦的問題?廢話不多說,先來看看這段程式碼:
// JCNavigator+JCTestModuleInterface.h@interface JCNavigator (JCTestModuleInterface)+ (void)openFirstLevelViewController; + (void)openSecondLevelViewController;@end// JCNavigator+JCTestModuleInterface.m@implementation JCNavigator (JCTestModuleInterface)+ (void)load { [[JCNavigator sharedNavigator] addModuleMap:[JCTestModuleMap new]]; } + (void)openFirstLevelViewController { [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey]; } + (void)openSecondLevelViewController { [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey]; }@end
從程式碼可以看出:
1)每個module宣告並實現對應的JCNavigator
類別;
2)在JCNavigator
類別中實現load類方法,新增對應子類化JCModuleMap
物件;
3)JCNavigator
類別標頭檔案中提供統一的對外介面,實現檔案中封裝內部呼叫細節,從而解決modules之間的耦合問題。-
下圖概述了實現過程:
Modules介面服務.jpg
總結
對於元件化的介紹,到這裡接近尾聲,整個解決方案有優點也有缺點,需要開發者各自權衡:
優點
1)支援Apps之間跳轉;
2)支援Modules之間跳轉及通訊(引數傳遞);
3)所有跳轉只基於JCNavigator
中間層框架;
4)可實現Modules之間解耦、互不依賴;
5)無需額外處理Modules頁面間的層級關係。缺點
1)需要子類化JCModuleMap
,並將例項化物件新增到JCNavigator
,增加了記憶體消耗;
2)如果viewController類名或傳遞的引數發生改變,Xcode不會報錯也沒有警告,需及時維護子類化JCModuleMap的實現,並更新JCNavigator類別中的呼叫程式碼。
關於框架的更多實現細節,請關注github開原始碼。
作者:joych
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3349/viewspace-2810711/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 簡述元件化解決方案CTMediator與MGJRouter的主要思想元件化
- Android 通用的元件自動註冊、自動初始化解決方案Android元件
- iOS第三發平臺元件化解耦實踐iOS元件化解耦
- IOS元件化方案總結iOS元件化
- 檔案自動化解決方案:Deliver Express for MacExpressMac
- 前端微服務化解決方案1-介紹前端微服務
- iOS全埋點解決方案-控制元件點選事件iOS控制元件事件
- 基於後編譯的國際化解決方案編譯
- 電池監控和最佳化解決方案Wattagio
- SAP智慧機器人流程自動化解決方案機器人
- 智和網管平臺國產化解決方案
- Smartbi:智慧園區資料視覺化解決方案視覺化
- 企業IT運維開發一體化解決方案運維
- 美麗鄉村-汙水治理數字化解決方案
- Laravel 7 訊息通知日期序列化解決方案Laravel
- 行業方案|醫療器械行業全鏈數字化解決方案行業
- 前端微服務化解決方案3 - 模組載入器前端微服務
- Android Native 記憶體洩漏系統化解決方案Android記憶體
- 大螢幕實時資料視覺化解決方案?視覺化
- 模切企業的業財一體化解決方案
- 光伏發電物聯網數字化解決方案
- 微火快報:門店數字化解決方案有哪些?
- 京東雲推出鄉村數智化解決方案SKA
- 智慧家居基於精益的數字化解決方案
- 前端微服務化解決方案2-使用必要性前端微服務
- 前端微服務化解決方案3-工程設計模式前端微服務設計模式
- 你要的全拿走-ocr文字識別智慧化解決方案
- TDengine 助力西門子輕量級數字化解決方案
- 化工企業安全風險管控數字化解決方案
- 家居行業基於精益的數字化解決方案行業
- 前端微服務化解決方案4-新模組構建前端微服務
- 前端單頁應用微服務化解決方案2 - Single-SPA前端微服務
- [AI開發]基於DeepStream的視訊結構化解決方案AI
- OpenVZ 9.0 - 基於容器的 Linux 開源虛擬化解決方案Linux
- OpenVZ 7.0 - 基於容器的 Linux 開源虛擬化解決方案Linux
- 基於 OpenYurt & EdgeX Foundry 的雲邊端一體化解決方案
- 數字孿生智慧學校三維視覺化解決方案視覺化
- iOS螢幕旋轉解決方案iOS