AppDelegate解耦之JSDecoupledAppDelegate

AppDelegate解耦


说到AppDelegate,大家想必都不陌生,它作为应用(UIApplication)的委托对象,在UIApplicationMain方法中被创建,当发生应用相关事件时,提供开发者响应的机会。

1
2
3
4
5
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

AppDelegateUIResponder的子类,应用将AppDelegate加入响应链中。UIApplication作为响应链中的最上层,当UIApplication任然无法处理特定事件时,会将事件转发给AppDelegate来处理。

当然,AppDelegate的主要职责还是响应应用事件,它满足UIApplicationDelegate协议,UIApplicationDelegate协议包含大量的委托方法,包括处理应用状态、状态恢复、后台下载数据、通知、URL Route等很多方面,这就会导致,随着程序的开发,AppDelegate将变得越来越臃肿,为了解决该问题,就需要进行解耦。

JSDecoupledAppDelegate


JSDecoupledAppDelegate的目的就是进行AppDelegate的解耦,接下来,将讲述一下实现原理。

首先为了实现解耦,需要将AppDelegate替换为定义好的JSDecoupledAppDelegate类(该类为单例模式),其相当于一个Proxy代理类,负责管理分发到不同的协议对象。JSDecoupledAppDelegateUIApplicationDelegate协议进行了分类拆分,生成如下的多个协议,针对每个协议,JSDecoupledAppDelegate都有一个满足该协议的属性,如@property (strong, nonatomic, nullable) id<JSApplicationStateDelegate> appStateDelegate;appStateDelegate用来存储满足JSApplicationStateDelegate协议的对象,当该协议定义的方法被调用时,将转发给委托对象进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
@protocol JSApplicationStateDelegate;
@protocol JSApplicationDefaultOrientationDelegate;
@protocol JSApplicationBackgroundFetchDelegate;
@protocol JSApplicationRemoteNotificationsDelegate;
@protocol JSApplicationLocalNotificationsDelegate;
@protocol JSApplicationStateRestorationDelegate;
@protocol JSApplicationURLResourceOpeningDelegate;
@protocol JSApplicationShortcutItemDelegate;
@protocol JSApplicationHealthDelegate;
@protocol JSApplicationProtectedDataDelegate;
@protocol JSApplicationWatchInteractionDelegate;
@protocol JSApplicationExtensionDelegate;
@protocol JSApplicationActivityContinuationDelegate;

比如,我们需要处理应用状态相关事件,新建一个处理对象,满足JSApplicationStateDelegate协议,在对象中定义+load方法,将JSDecoupledAppDelegateappStateDelegate属性赋给自己,如下所示,再在对象中实现JSApplicationStateDelegate方法,这样,当应用状态相关事件产生时,该对象就能收到相关事件。

1
2
3
4
+ (void)load
{
[JSDecoupledAppDelegate sharedAppDelegate].appStateDelegate = [[self alloc] init];
}

这样,就实现了AppDelegate的解耦,针对不同的应用事件类别,由不同的对象进行处理。

有的人就好奇了,JSDecoupledAppDelegate是怎么实现事件分发的呢?

JSDecoupledAppDelegate重载了- (BOOL)respondsToSelector:(SEL)aSelector方法,为什么会选择重载这个方法呢,我们再来思考一下,平时我们实现Delegate委托方式时,当我们需调用委托对象的某个方法时,首先需要做的就是判断一下委托对象是否实现了该方法,即使用respondsToSelector:(如果不进行提前判断,当委托对象没有实现该方法时,将导致Crash),这也就不难解释为什么选择重载该方法了。

直接show code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (BOOL)respondsToSelector:(SEL)aSelector
{
//获取委托属性名,每个协议对应一个属性,如appStateDelegate
NSArray *delegateProperties = JSApplicationDelegateProperties();

// 1. 获取定义了aSelector的协议
__block BOOL protocolFound = NO;
__block BOOL delegateRespondsToSelector = NO;

[JSApplicationDelegateSubprotocols() enumerateObjectsUsingBlock:^(NSString *protocolName, NSUInteger idx, BOOL *stop) {
//获取协议定义的方法
NSSet *protocolMethods = JSSelectorListInProtocol(NSProtocolFromString(protocolName));

const BOOL methodCorrespondsToThisProtocol = [protocolMethods containsObject:NSStringFromSelector(aSelector)];

if (methodCorrespondsToThisProtocol)
{
protocolFound = YES;

// 2. 获取协议委托对象,如appStateDelegate属性
id delegateObjectForProtocol = [self valueForKey:delegateProperties[idx]];

//判断委托对象是否实现了该方法
delegateRespondsToSelector = [delegateObjectForProtocol respondsToSelector:aSelector];

*stop = YES;
}
}];

if (protocolFound)
{
// 3. 返回委托对象是否能响应该方法
return delegateRespondsToSelector;
}
else
{
// 4. 如不能响应,走缺省方法处理
return [super respondsToSelector:aSelector];
}
}