简介
看到这个话题,自然是想整理AppDelegate里面各自杂乱的逻辑。
目标
- 各个模块可以单独实现,不需要在AppDelegate添加代码或引用其它模块
- 不同模块可以处理
application:openURL:options:
类似多参数问题 - 模块之间调用有可能出现顺序关系
- 只用一套实现方式,使用方便简单
当前存在方案对比
- Notification
- Method Swizzling
关于 Notification
@interface MTModuleA : NSObject
@end
@implementation MTModuleA
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"UIApplicationDidBecomeActiveNotification");
}];
});
}
@end
有段时间我使用了很多类似的实现来为AppDelegate瘦身。
优点
- 模块化是比较不错的
- 完全没有相互依赖
- 使用简单方便
缺点
- 不能很快的找到对应实现地方
- 不好实现 ModuleA 必须在 ModuleB 模块之后
- 无法处理
application:openURL:options:
或多参数问题 - 监听
UIApplicationDidEnterBackgroundNotification
然后多线程操作会有问题,但是如果AppDelegate 中 hook 方法applicationDidEnterBackground:
是正常的(原因目前不明)
PS1:使用命名的来规范比如对于不同模块使用 AppDelegateModuleA 或 AppDelegate + ModuleA 的方式确实可以解决一部分模块问题,但是实际开发中经常有业务是应用变成活跃状态,需要发起网络请求,如果这时也通过命名的话就比较奇怪了
PS2:可以在AppDelegate中再定义一套通知 比如
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
NSMutableDictionary *object = [NSMutableDictionary dictionary];
if (app) object[MTApplicationOpenURLKeyApplication] = app;
if (url) object[MTApplicationOpenURLKeyURL] = url;
if (options) object[MTApplicationOpenURLKeyOptions] = options;
[[NSNotificationCenter defaultCenter] postNotificationName:MTApplicationOpenURLNotification object:object];
return YES;
}
相比使用系统的通知,我更加建议自己定义一套通知,就是繁琐了些,除了顺序问题暂时不能控制,其它的都可以解决了
关于 Method Swizzling
为了防止自己写 Method Swizzling 这些繁琐的方法我们可以直接使用 Aspects 这个库来实现
@interface MTModuleB : NSObject
@end
@implementation MTModuleB
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[AppDelegate aspect_hookSelector:@selector(applicationDidBecomeActive:) withOptions:AspectPositionAfter usingBlock: ^{
NSLog(@"UIApplicationDidBecomeActiveNotification");
}error:nil];
});
}
@end
相比 Notification 的实现方式还是更加优雅些,除了顺序不能控制其它问题也都解决了
缺点
- 必须在AppDelegate实现待hook的方式,所以看着一个空方法,又不能删掉,还是挺郁闷的事
- 由于 Aspects 是比较通用的库,使用的地方比较多,所以定位全部的AppDelegate时不是那么方便
一种新的实现方式
设计思路:实现一个单列中介者,每个需要回调的模块或对象在合适的时机向中介者注册,AppDelegate中每当方法触发了就通知中介者,然后中介者把注册的对象调用一遍
这里涉及到一个工具类实现,一个selector可以对应多个target回调问题,target以弱引用的方式存储。这里可以参考我之前实现的 MTMultiTargetCallBack
@interface MTMultiTargetCallBack : NSObject
@property (nonatomic, copy, readonly) NSArray *targets;
- (void)addTarget:(id)target;
- (void)removeTarget:(id)target;
- (void)removeAllTargets;
- (BOOL)containsTarget:(id)target;
//第一个参数为可以为nil,之后的参数支持nil,id,int,unsigned int,long,unsigned long,long long,unsigned long long,double,BOOL
- (void)callBackSelector:(SEL)selector params:(id)param, ...;
- (void)callBackSelector:(SEL)selector;
- (void)callBackIfExistSelector:(SEL)selector params:(id)param, ...;
- (void)callBackIfExistSelector:(SEL)selector;
@end
有了 MTMultiTargetCallBack 他来实现中这种方式就方便很多了
中介者
@interface MTAppDelegateProxy : NSObject
+ (instancetype)shared;
@property (nonatomic, readonly) MTMultiTargetCallBack *multiTarget;
@end
AppDelegate.m
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[MTAppDelegateProxy shared].multiTarget callBackSelector:@selector(applicationDidBecomeActive:) params:application];
}
模块
@interface MTModuleC : NSObject<UIApplicationDelegate>
@end
@implementation MTModuleC
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[MTAppDelegateProxy shared].multiTarget addTarget:self];
});
}
+ (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(@"applicationDidBecomeActive");
}
@end
这种方式可以解决全部需求了,关于顺序的处理由于有了中介者,实现方式就很多了,比如注册时提供一个优先级,可以参考 NSOperationQueuePriority
网友评论