關於iOS
的模塊化,要追溯到16年接觸的BeeHive
了,BeeHive
將功能模塊化,以module
的形式進行構建,以performSelector:
的形式進行module
的事件響應,以protocol
的形式進行module間的通訊。能夠說思路很是清晰明瞭了。關於BeeHive
的代碼傳送門alibaba/BeeHive,star已3.2k,關於BeeHive源碼解析可參考霜神文章傳送門BeeHive —— 一個優雅但還在完善中的解耦框架。實際上我並不認爲BeeHive
能夠真正用到咱們項目中來,它確實構建了module
,可是module實例
帶來的內存問題會讓人頭疼。我的認爲將BeeHive
思想中的module
部分改造一下用在咱們的AppDelegate
中是徹底可行的。下面進入正文。git
畫了一個結構圖,module1到module4爲咱們須要在Appdelegate中進行處理的業務邏輯,好比說咱們的數據庫處理
,分享功能
,推送功能
等等。 github
@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.bash
@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建立部分:app
@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:sel
withObject:application
withObject: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)eventSel
Complete:(void(^)(id module,SEL sel))complete;
/**
移除module對象
@param moduleID module ID
*/
- (void)removeModule:(NSString *)moduleID;
複製代碼
模塊註冊的思路是徹底按照BeeHive
的思想來的,在編譯期就將咱們的module
經過__attribute
函數進行註冊。在運行期再將咱們註冊好的module
取出來在registedAllModules
中進行實例化,按照level
的返回值進行排序存儲。__attribute
函數具體作了什麼能夠參考我以前的文章寫一個易於維護使用方便性能可靠的Hybrid框架(三)—— 配置插件關於插件註冊部分的解釋。post
handleApplicationEvent:Complete:
爲module事件響應的核心函數:
- (void)handleApplicationEvent:(SEL)eventSel
Complete:(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
函數,而且將參數傳遞過去。
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
一旦細化,什麼時候銷燬也是讓開發者頭疼的問題。先說這麼多,各位小夥伴有任何問題歡迎評論區討論。
最後附上傳送門:《SHRMAppDelegate》