前言
關于iOS
的模塊化,要追溯到16年接觸的BeeHive
了,BeeHive
將功能模塊化,以module
的形式進行構建,以performSelector:
的形式進行module
的事件響應,以protocol
的形式進行module間的通信。可以說思路非常清晰明了了。關于BeeHive
的代碼傳送門alibaba/BeeHive,star已3.2k,關于BeeHive源碼解析可參考霜神文章傳送門BeeHive —— 一個優雅但還在完善中的解耦框架。實際上我并不認為BeeHive
可以真正用到我們項目中來,它確實構建了module
,但是module實例
帶來的內存問題會讓人頭疼。個人認為將BeeHive
思想中的module
部分改造一下用在我們的AppDelegate
中是完全可行的。下面進入正文。
目錄
一、模塊拆分
二、模塊事件響應
三、模塊管理
-
1.模塊注冊
-
2.觸發event
-
3.移除module
四、總結
一、模塊拆分
畫了一個結構圖,module1到module4為我們需要在Appdelegate中進行處理的業務邏輯,比如說我們的數據庫處理
,分享功能
,推送功能
等等。
@protocol SHRMAppEventModuleProtocol <UIApplicationDelegate>- (NSInteger)moduleLevel;
- (void)destroyModule;
- (NSString *)moduleID;@end
復制代碼
接口定義了三個函數,moduleLevel
返回module執行的優先級,destroyModule
用來對module進行釋放,moduleID
返回當前module的id。接口的默認實現統一在BaseAppEventModule
中進行。BaseAppEventModule
為所有module的父類,只有繼承了BaseAppEventModule
的module才能被管理。
關于BaseAppEventModule
的默認實現也很簡單,對module進行銷毀的時候用到了SHRMAppEventModuleManager
下面會講到,優先級默認設置100.
@interface SHRMBaseAppEventModule : NSObject <SHRMAppEventModuleProtocol>@end#define MODULE_LEVEL_DEFAULT 100
@implementation SHRMBaseAppEventModule- (NSInteger)moduleLevel {return MODULE_LEVEL_DEFAULT;
}- (void)destroyModule {[[SHRMAppEventModuleManager sharedInstance] removeModule:[self moduleID]];NSLog(@"%@ destroy",NSStringFromClass([self class]));
}- (NSString *)moduleID {return NSStringFromClass([self class]);
}@end
復制代碼
模塊的創建上面說到了必須要繼承自SHRMBaseAppEventModule
,只有繼承了SHRMBaseAppEventModule
的module才會被管理,因為只有SHRMBaseAppEventModule
遵循了SHRMAppEventModuleProtocol
協議。關于module創建部分:
@interface testMudule : SHRMBaseAppEventModule
@end@implementation testMudule- (NSInteger)moduleLevel {return 1;
}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[self initMudule];[self destroyModule];return YES;
}- (void)initMudule {NSLog(@"testMudule init");
}@end
復制代碼
application: didFinishLaunchingWithOptions:
函數的實現展示了一個module的整個生命周期,從創建到銷毀的過程,那么application: didFinishLaunchingWithOptions:
是怎么響應的,實際上module的頭文件并沒有暴漏任何接口,到這里就實現了功能的模塊化
。那為什么還能執行到這里,這要感謝強大的runtime
函數performSelector:
,下面講一下我對module
事件響應的處理。
二、模塊事件響應
還是以上面的application: didFinishLaunchingWithOptions:
函數為例,它是怎么來的,很明顯這是AppDelegate
里面的APP生命周期回調:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[[SHRMAppEventModuleManager sharedInstance] handleApplicationEvent:@selector(application:didFinishLaunchingWithOptions:)Complete:^(id _Nonnull module, SEL _Nonnull sel) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"[module performSelector:selwithObject:applicationwithObject:launchOptions];
#pragma clang diagnostic pop}];return YES;
}
復制代碼
沒看錯,這樣一搞AppDelegate
的application: didFinishLaunchingWithOptions:
就剩這些了,這樣一來,所有實現了application: didFinishLaunchingWithOptions:
的modlue都會被調用,調用優先級根據接口定義的moduleLevel
返回值確定。到這里我們就完成了AppDelegate瘦身
的工作,實際上AppDelegate中的其他回調處理是一樣的。當然還有一個很重要的沒有說,就是SHRMAppEventModuleManager
做了什么,通過上面的結構圖能夠看到SHRMAppEventModuleManager
是個中間件,用來處理module與AppDelegate間的關系。下面說到第三部分,模塊管理。
三、模塊管理
SHRMAppEventModuleManager
為一個單例,提供了三個接口:
/**初始化所有的AppDelegate相關的Event Modules*/
- (void)registedAllModules;/**觸發event module處理AppDelegate回調事件@param eventSel AppDelegate 回調事件消息@param complete module處理handle*/
- (void)handleApplicationEvent:(SEL)eventSelComplete:(void(^)(id module,SEL sel))complete;/**移除module對象@param moduleID module ID*/
- (void)removeModule:(NSString *)moduleID;
復制代碼
1.模塊注冊
模塊注冊的思路是完全按照BeeHive
的思想來的,在編譯期就將我們的module
通過__attribute
函數進行注冊。在運行期再將我們注冊好的module
取出來在registedAllModules
中進行實例化,按照level
的返回值進行排序存儲。__attribute
函數具體做了什么可以參考我之前的文章寫一個易于維護使用方便性能可靠的Hybrid框架(三)—— 配置插件關于插件注冊部分的解釋。
2.觸發event
handleApplicationEvent:Complete:
為module事件響應的核心函數:
- (void)handleApplicationEvent:(SEL)eventSelComplete:(void(^)(id module,SEL sel))complete {NSMutableArray *tmpAppEventModules = [[NSMutableArray alloc] initWithArray:self.appEventModules];for (id<SHRMAppEventModuleProtocol>module in tmpAppEventModules){if ([module conformsToProtocol:@protocol(SHRMAppEventModuleProtocol)]){if ([module respondsToSelector:eventSel]) {if (complete) {complete(module,eventSel);}}}}
}
復制代碼
if ([module respondsToSelector:eventSel])
就會執行complete
將module
和sel
返回,也就是到了AppDelegate
里面,繼而執行module
的sel
函數,并且將參數傳遞過去。
3.移除module
module
的移除,在AppDelegate
里面,我們將程序啟動之后調用完就不再使用的功能module會手動執行移除操作。這也是前言所說的BeeHive在module移除這一塊會稍顯復雜,但是在AppDelegate里面,我們是完全可以知道哪些module在加載之后可以立即移除的,另外我們僅在AppDelegate中進行模塊化,產生的module實例也會非常少,so,完全沒必要擔心module所帶來的內存開銷問題。
- (void)removeModule:(NSString *)moduleID {NSInteger index = NSNotFound;NSInteger resIndex = 0;for (id<SHRMAppEventModuleProtocol>module in self.appEventModules){if ([[module moduleID] isEqualToString:moduleID]){index = resIndex;break;}resIndex++;}if (index != NSNotFound) {[self.appEventModules removeObjectAtIndex:index];}
}
復制代碼
總結
最后總結一下,關于模塊化,現在總體來看比較流行,也有很多介紹模塊化,組件化具體實施之路的文章,都很優秀,也值得學習。關于解耦,我更傾向于protocol
的方式,接口protocol
化,代碼易讀且清晰。之前看過mrpeak
在組件化方面的文章,傳送門iOS 組件化方案,個人覺得protocol+version
的方案和BeeHive
非常像,protocol
解耦,version
進行module
的版本管理,但是還是沒有解決module
所帶來的內存開銷問題,module
一旦細化,何時銷毀也是讓開發者頭疼的問題。先說這么多,各位小伙伴有任何問題歡迎評論區討論。
最后附上DEMO傳送門:AppDelegateMudule,歡迎star?。