關於AppDelegate瘦身的多種解決方案

在iOS項目的開發中,AppDelegate是一個耦合發生的重災地,不少項目的開發時間一長,AppDelegate就不可避免地出現,代碼臃腫,調用順序混亂,邏輯複雜的問題。這個UIApplication的委託類,做爲一個常駐內存的單例,它承載了太多太多的功能,連蘋果的官方文檔都建議應該由AppDelegate來處理這些工做:git

  • 1.app的啓動代碼;
  • 2.響應app的狀態,好比app切換到後臺和前臺等狀態;
  • 3.響應外部傳遞給app的通知,好比說push,low-memory warnings;
  • 4.決定了app的狀態是否應該保存或者恢復;
  • 5.響應不是發送給特定view或者vc,而是發送給app自己的事件;
  • 6.用來保存一些不屬於特定vc的數據。

不得不吐槽一句,有時候,蘋果的官方文檔的建議也不那麼靠譜啊🤦‍♀️。github

一個業務邏輯稍複雜點的項目,上述6點的全部功能的代碼直接一股腦塞到一個文件裏,能不臃腫纔怪了。c#

這裏介紹三種給AppDelegate瘦身的方式:bash

NSNotification

咱們知道一個app的各類事件發生時除了會調用UIApplicationDelegate中的方法,同時還會發送一個NSNotification,蘋果在UIApplication.h中聲明瞭這些通知。可是並非全部方法都有對應的通知,這時咱們能夠仿照蘋果的命名規範補上未定義的這些方法對應的通知,而後在本身的AppDelegate中顯式地發送它們。架構

好比,定義application:didRegisterUserNotificationSettings:方法對應的通知:app

UIKIT_EXTERN NSNotificationName const UIApplicationDidRegisterUserNotificationSettingsNotification;
複製代碼

同時別忘了定義參數對應的key:模塊化

UIKIT_EXTERN NSString *const UIApplicationUserNotificationSettingsKey;
複製代碼

而後在AppDelegate中的application:didRegisterUserNotificationSettings:方法裏執行:組件化

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationUserNotificationSettingsKey object:[UIApplication sharedApplication] userInfo:@{UIApplicationUserNotificationSettingsKey : notificationSettings}];
}
複製代碼

最後在須要響應這個事件的模塊中註冊通知,處理對應的業務邏輯便可。post

ModuleManager

經過實現ModuleManager類,來管理項目中的模塊,首先在軟件啓動時經過讀取配置文件(一般用plist)讀取模塊,在AppDelegate的每一個事件接收到響應的時,在對應方法中逐一調用已註冊的模塊對應的響應方法: 首先在啓動時load modulesui

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[ModuleManager sharedInstance] loadModulesFromPlist:[[NSBundle mainBundle] pathForResource:@"modules" ofType:@"plist"]];
}
複製代碼

UIApplication event發生時,依次調用每一個module的對應方法:

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSArray<id<ModuleProtocol>> *modules = [ModuleManager sharedInstance].modules;
    [modules enumerateObjectsUsingBlock:^(id<ModuleProtocol>  _Nonnull module, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([module respondsToSelector:_cmd]) {
            [module applicationDidBecomeActive:application];
        }
    }];
}
複製代碼

ModuleProtocol繼承自UIApplicationDelegate,定義以下:

@protocol ModuleProtocol <UIApplicationDelegate>

//在這裏根據本身項目的業務定義模塊的共有方法

@end
複製代碼

AOP

AOP在Objective-C中利用method swizzle來實現,例子就不舉了,相信有必定經驗的同窗應該都知道到底要如何實現。

對比

以上三種方法,從結果上來講均可以達成給AppDelegate瘦身的目的,可是各有優缺點,所以也會用在不一樣場景,我說一下本身的我的見解,拋磚引玉:

NSNotification

  • 使用NSNotification的好處在於,若是不須要響應系統未定義的通知,那麼你的AppDelegate裏甚至能夠一行代碼都不用寫,瘦身瘦成一道閃電!
  • 你沒法管理通知的註冊者的調用順序,好比說你建立rootwindow的代碼若是還依賴讀取配置模塊,可是分別使用觀察通知將實現代碼寫在了不一樣模塊中,這時候就很難保證讀取配置模塊在建立rootwindow以前執行。所以爲了保證調用順序,能夠保留部分代碼在AppDelegate中。

ModuleManager

  • ModuleManager更適合高度組件化/模塊化的項目,項目到了這個階段,工程中的任何功能都被封裝成了模塊,所以能夠經過調整plist文件中的模塊順序,來保證模塊的執行順序是正確的。
  • 相對的,使用ModuleManager的設計,對工程代碼的質量要求也會比較高,若是你的項目還未開始進行組件化/模塊化的設計,使用這種方式來給AppDelegate瘦身的難度也會很大,此時選用NSNotification是更好的解決方案。

AOP

  • 使用AOP的須要慎重和當心,這一點在不少的技術博客和書籍中都會有提到,AOP用的好會是一把架構的利劍,幫你披荊斬棘,可是用很差的話,也會傷到本身。
  • AOP一樣很很差控制代碼的執行順序。
  • 利用AOP因爲能夠作到百分百的無侵入,所以很適合在作第三方SDK項目時使用,這樣可以儘量減小SDK的使用者的接入成本。
  • AOP能夠結合NSNotification或ModuleManager使用,而後者達到徹底免侵入(僅僅適合極端的代碼潔癖主義者)。

以上三種方法,並無真正意義上的孰優孰劣,而是須要根據本身項目的特色來選擇更適合的方案。 最近在重構項目時,我結合AOP和NSNotification寫了一個小功能(https://github.com/jiaopen/AppDelegateExtensions),幾乎無侵入地解決AppDelegate臃腫問題。

Features:

  • AOP沒有使用category來實現methodswizzle,由於不是全部工程的AppDelegate起名相同,所以須要在load中顯式調用一行註冊代碼。
  • 增長了經常使用的UIApplicationDelegate方法對應的通知,能夠根據自已業務的狀況補充。

歡迎糾錯,拍磚。

相關文章
相關標籤/搜索