AppDelegate瘦身

2018-02-06  本文已影响91人  mtry

简介

看到这个话题,自然是想整理AppDelegate里面各自杂乱的逻辑。

目标

  1. 各个模块可以单独实现,不需要在AppDelegate添加代码或引用其它模块
  2. 不同模块可以处理application:openURL:options:类似多参数问题
  3. 模块之间调用有可能出现顺序关系
  4. 只用一套实现方式,使用方便简单

当前存在方案对比

  1. Notification
  2. 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瘦身。

优点

  1. 模块化是比较不错的
  2. 完全没有相互依赖
  3. 使用简单方便

缺点

  1. 不能很快的找到对应实现地方
  2. 不好实现 ModuleA 必须在 ModuleB 模块之后
  3. 无法处理 application:openURL:options: 或多参数问题
  4. 监听 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 的实现方式还是更加优雅些,除了顺序不能控制其它问题也都解决了

缺点

  1. 必须在AppDelegate实现待hook的方式,所以看着一个空方法,又不能删掉,还是挺郁闷的事
  2. 由于 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

上一篇 下一篇

猜你喜欢

热点阅读