Fork me on GitHub

AppDelegate瘦身实战

AppDelegate的瘦身是一个常谈的话题,而且网络上也各有各家的说法,但是一般都是采用两种方式实现AppDelegate的瘦身,一种是从AppDelegate本身入手,通过各种办法减少AppDelegate的代码;另外一种是从架构层面入手了。

从AppDelegate本身入手

目前采用的实践方案有3种,分别为:

  • FRDModuleManager
  • JSDecoupledAppDelegate
  • category分类

FRDModuleManager

FRDModuleManager是豆瓣开源的一个轻量级模块管理工具。通过注册模块类,遵守协议来拆分AppDelegate的指责,这样AppDelegate就更容易维护,而且它的核心类FRDModuleManager不到400行的代码,很容易理解,使用也很简单。

  • 加载注册模块文件
1
2
3
4
5
// 获取AppDelegate的配置,并且执行相关方法
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"ModulesRegister" ofType:@"plist"];
FRDModuleManager *mgr = [FRDModuleManager sharedInstance];
[mgr loadModulesWithPlistFile:plistPath];
[mgr application:application didFinishLaunchingWithOptions:launchOptions];
  • 遵守协议,实现UIApplication的生命周期方法即可
1
2
3
4
5
6
7
8
9
10
#import <Foundation/Foundation.h>
#import "FRDModuleManager.h"

NS_ASSUME_NONNULL_BEGIN

@interface LJNUMModule : NSObject<FRDModule>

@end

NS_ASSUME_NONNULL_END
  • 实现原理

FRDModuleManager的实现原理很简单,其本身遵守UIApplicationDelegate协议,并且内部数组维护一个注册的模块信息,在UIApplication的生命周期方法内,调用FRDModuleManager的相应方法,遍历内部注册的模块,在调用对应模块的相应方法

  • 优点
    • 使用简单方便
    • 注册的模块,可以享受AppDelegate的整个生命周期
  • 缺点
    • 每个注册模块要初始化并分配内存,如果注册了大量模块,注定要创建大量对象并影响App启动速度
    • 如果模块之前存在依赖问题,那么初始化会出现问题

JSDecoupledAppDelegate

JSDecoupledAppDelegate是由JaviSoto作者开发的一款轻量级的AppDelegate解耦工具,他将AppDelegate的各个功能点独立出来,并通过代理的方式将控制权下发。我们可以看到JSDecoupledAppDelegate中有很多代理,简单列举几个

代理名称 协议 描述
appStateDelegate JSApplicationStateDelegate App各种状态
appDefaultOrientationDelegate JSApplicationDefaultOrientationDelegate App通知代理
remoteNotificationsDelegate JSApplicationRemoteNotificationsDelegate App通知代理

这些代理见名知意,例如appStateDelegate是用于处理App的各种状态(didFinishLaunchingWithOptions、applicationDidBecomeActive等)下的逻辑;remoteNotificationsDelegate是用于处理App的推送的逻辑。JSDecoupledAppDelegate使用起来也非常简单:

  • 在main.m中将AppDelegate替换成JSDecoupledAppDelegate
1
return UIApplicationMain(argc, argv, nil, NSStringFromClass([JSDecoupledAppDelegate class]));
  • 创建一个继承自NSObject的类LJNApplication并遵守JSApplicationStateDelegate协议,在实现类中添加如下代码
1
2
3
4
5
6
7
8
9
10
11
12
+ (void)load
{
LJNApplicationStateDelegate *ap = [[self alloc] init];
[JSDecoupledAppDelegate sharedAppDelegate].appStateDelegate = ap;
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
{
NSLog(@"LJNApplicationStateDelegate模块:%@",NSStringFromSelector(_cmd));
return YES;
}
  • 实现原理

如果你对iOS的消息转发机制有所了解的话,JSDecoupledAppDelegate就是通过转发AppDelegate的各个方法来实现的。

  • 优点
    • 由于各个子代理对象的执行顺序是确定的,因此可以解决FRDModuleManager的相互依赖问题
  • 缺点
    • 由于废弃了原生的AppDelegate,所以我们不能再向以前一样获取windonw,以及windowrootViewController,只能通过view.window曲线救国
((AppDelegate *)[UIApplication sharedApplication].delegate).window

AppDelegate分类

创建AppDelegate分类无疑是低投入高产出的最好解决方案了。目前公司的项目正在使用该方式。不需要添加任何三方库,我们就可以给AppDelegate添加很多方法,并且能轻松控制方法的执行顺序。

然而分类的缺点也不言而喻:添加新的属性比较繁琐,只能通过runtime或者BlocksKit等三方库实现。

以上三种方法都是通过对AppDelegate修改或添加的方式来达到降低耦合的