iOS 代碼優化 -- AppDelegate模塊化瘦身

前言

關於iOS的模塊化,要追溯到16年接觸的BeeHive了,BeeHive將功能模塊化,以module的形式進行構建,以performSelector:的形式進行module的事件響應,以protocol的形式進行module間的通訊。能夠說思路很是清晰明瞭了。關於BeeHive的代碼傳送門alibaba/BeeHive,star已3.2k,關於BeeHive源碼解析可參考霜神文章傳送門BeeHive —— 一個優雅但還在完善中的解耦框架。實際上我並不認爲BeeHive能夠真正用到咱們項目中來,它確實構建了module,可是module實例帶來的內存問題會讓人頭疼。我的認爲將BeeHive思想中的module部分改造一下用在咱們的AppDelegate中是徹底可行的。下面進入正文。git

目錄

1、模塊拆分

2、模塊事件響應

3、模塊管理

  • 1.模塊註冊
  • 2.觸發event
  • 3.移除module

4、總結

1、模塊拆分

畫了一個結構圖,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事件響應的處理。框架

2、模塊事件響應

仍是以上面的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;
}
複製代碼

沒看錯,這樣一搞AppDelegateapplication: didFinishLaunchingWithOptions:就剩這些了,這樣一來,全部實現了application: didFinishLaunchingWithOptions:的modlue都會被調用,調用優先級根據接口定義的moduleLevel返回值肯定。到這裏咱們就完成了AppDelegate瘦身的工做,實際上AppDelegate中的其餘回調處理是同樣的。固然還有一個很重要的沒有說,就是SHRMAppEventModuleManager作了什麼,經過上面的結構圖可以看到SHRMAppEventModuleManager是個中間件,用來處理module與AppDelegate間的關係。下面說到第三部分,模塊管理。函數

3、模塊管理

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框架(三)—— 配置插件關於插件註冊部分的解釋。post

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])就會執行completemodulesel返回,也就是到了AppDelegate裏面,繼而執行modulesel函數,而且將參數傳遞過去。

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一旦細化,什麼時候銷燬也是讓開發者頭疼的問題。先說這麼多,各位小夥伴有任何問題歡迎評論區討論。

最後附上傳送門:《SHRMAppDelegate》

相關文章
相關標籤/搜索