AppDelegate的模組化+瘦身
關於 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
一旦細化,何時銷燬也是讓開發者頭疼的問題。先說這麼多,各位看官有任何問題歡迎評論區討論。