前言
關於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: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;
複製程式碼
1.模組註冊
模組註冊的思路是完全按照BeeHive
的思想來的,在編譯期就將我們的module
通過__attribute
函式進行註冊。在執行期再將我們註冊好的module
取出來在registedAllModules
中進行例項化,按照level
的返回值進行排序儲存。__attribute
函式具體做了什麼可以參考我之前的文章寫一個易於維護使用方便效能可靠的Hybrid框架(三)—— 配置外掛關於外掛註冊部分的解釋。
2.觸發event
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
函式,並且將引數傳遞過去。
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?。