41

iOS 代码优化 -- AppDelegate模块化瘦身

 4 years ago
source link: https://www.tuicool.com/articles/vEZ7zuF
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

关于 iOS 的模块化,要追溯到16年接触的 BeeHive 了, BeeHive 将功能模块化,以 module 的形式进行构建,以 performSelector: 的形式进行 module 的事件响应,以 protocol 的形式进行module间的通信。可以说思路非常清晰明了了。关于 BeeHive 的代码传送门 alibaba/BeeHive ,star已3.2k,关于BeeHive源码解析可参考霜神文章传送门 BeeHive —— 一个优雅但还在完善中的解耦框架 。实际上我并不认为 BeeHive 可以真正用到我们项目中来,它确实构建了 module ,但是 module实例 带来的内存问题会让人头疼。个人认为将 BeeHive 思想中的 module 部分改造一下用在我们的 AppDelegate 中是完全可行的。下面进入正文。

目录

一、模块拆分

二、模块事件响应

三、模块管理

  • 1.模块注册

  • 2.触发event

  • 3.移除module

四、总结

一、模块拆分

画了一个结构图,module1到module4为我们需要在Appdelegate中进行处理的业务逻辑,比如说我们的 数据库处理分享功能推送功能 等等。

QR32eeM.png!web 首先为所有模块定义了三个接口:
@protocol SHRMAppEventModuleProtocol <UIApplicationDelegate>

- (NSInteger)moduleLevel;
- (void)destroyModule;
- (NSString *)moduleID;

@end
复制代码

接口定义了三个函数, moduleLevel 返回module执行的优先级, destroyModule 用来对module进行释放, moduleID 返回当前module的id。接口的默认实现统一在 BaseAppEventModule 中进行。 BaseAppEventModule 为所有module的父类,只有继承了 BaseAppEventModule 的module才能被管理。

关于 BaseAppEventModule 的默认实现也很简单,对module进行销毁的时候用到了 SHRMAppEventModuleManager 下面会讲到,优先级默认设置100.

@interface SHRMBaseAppEventModule : NSObject <SHRMAppEventModuleProtocol>

@end


#define MODULE_LEVEL_DEFAULT 100
@implementation SHRMBaseAppEventModule

- (NSInteger)moduleLevel {
    return MODULE_LEVEL_DEFAULT;
}

- (void)destroyModule {
    [[SHRMAppEventModuleManager sharedInstance] removeModule:[self moduleID]];
    NSLog(@"%@ destroy",NSStringFromClass([self class]));
}

- (NSString *)moduleID {
    return NSStringFromClass([self class]);
}

@end
复制代码

模块的创建上面说到了必须要继承自 SHRMBaseAppEventModule ,只有继承了 SHRMBaseAppEventModule 的module才会被管理,因为只有 SHRMBaseAppEventModule 遵循了 SHRMAppEventModuleProtocol 协议。关于module创建部分:

@interface testMudule : SHRMBaseAppEventModule
@end

@implementation testMudule

- (NSInteger)moduleLevel {
    return 1;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self initMudule];
    [self destroyModule];
    return YES;
}

- (void)initMudule {
    NSLog(@"testMudule init");
}

@end
复制代码

application: didFinishLaunchingWithOptions: 函数的实现展示了一个module的整个生命周期,从创建到销毁的过程,那么 application: didFinishLaunchingWithOptions: 是怎么响应的,实际上module的头文件并没有暴漏任何接口,到这里就实现了功能的 模块化 。那为什么还能执行到这里,这要感谢强大的 runtime 函数 performSelector: ,下面讲一下我对 module 事件响应的处理。

二、模块事件响应

还是以上面的 application: didFinishLaunchingWithOptions: 函数为例,它是怎么来的,很明显这是 AppDelegate 里面的APP生命周期回调:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [[SHRMAppEventModuleManager sharedInstance] handleApplicationEvent:@selector(application:didFinishLaunchingWithOptions:)
                                                              Complete:^(id  _Nonnull module, SEL  _Nonnull sel) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [module performSelector:sel
                     withObject:application
                     withObject:launchOptions];
#pragma clang diagnostic pop
    }];
    return YES;
}
复制代码

没看错,这样一搞 AppDelegateapplication: didFinishLaunchingWithOptions: 就剩这些了,这样一来,所有实现了 application: didFinishLaunchingWithOptions: 的modlue都会被调用,调用优先级根据接口定义的 moduleLevel 返回值确定。到这里我们就完成了AppDelegate 瘦身 的工作,实际上AppDelegate中的其他回调处理是一样的。当然还有一个很重要的没有说,就是 SHRMAppEventModuleManager 做了什么,通过上面的结构图能够看到 SHRMAppEventModuleManager 是个中间件,用来处理module与AppDelegate间的关系。下面说到第三部分,模块管理。

三、模块管理

SHRMAppEventModuleManager 为一个单例,提供了三个接口:

/**
 初始化所有的AppDelegate相关的Event Modules
 */
- (void)registedAllModules;

/**
 触发event module处理AppDelegate回调事件
 
 @param eventSel AppDelegate 回调事件消息
 @param complete module处理handle
 */
- (void)handleApplicationEvent:(SEL)eventSel
                      Complete:(void(^)(id module,SEL sel))complete;

/**
 移除module对象
 
 @param moduleID module ID
 */
- (void)removeModule:(NSString *)moduleID;
复制代码

1.模块注册

模块注册的思路是完全按照 BeeHive 的思想来的,在编译期就将我们的 module 通过 __attribute 函数进行注册。在运行期再将我们注册好的 module 取出来在 registedAllModules 中进行实例化,按照 level 的返回值进行排序存储。 __attribute 函数具体做了什么可以参考我之前的文章 写一个易于维护使用方便性能可靠的Hybrid框架(三)—— 配置插件 关于插件注册部分的解释。

2.触发event

handleApplicationEvent:Complete: 为module事件响应的核心函数:

- (void)handleApplicationEvent:(SEL)eventSel
                      Complete:(void(^)(id module,SEL sel))complete {
   
    NSMutableArray *tmpAppEventModules = [[NSMutableArray alloc] initWithArray:self.appEventModules];
    for (id<SHRMAppEventModuleProtocol>module in tmpAppEventModules)
    {
        if ([module conformsToProtocol:@protocol(SHRMAppEventModuleProtocol)])
        {
            if ([module respondsToSelector:eventSel]) {
                if (complete) {
                    complete(module,eventSel);
                }
            }
        }
    }
}
复制代码

if ([module respondsToSelector:eventSel]) 就会执行 completemodulesel 返回,也就是到了 AppDelegate 里面,继而执行 modulesel 函数,并且将参数传递过去。

3.移除module

module 的移除,在 AppDelegate 里面,我们将程序启动之后调用完就不再使用的功能module会手动执行移除操作。这也是前言所说的BeeHive在module移除这一块会稍显复杂,但是在AppDelegate里面,我们是完全可以知道哪些module在加载之后可以立即移除的,另外我们仅在AppDelegate中进行模块化,产生的module实例也会非常少,so,完全没必要担心module所带来的内存开销问题。

- (void)removeModule:(NSString *)moduleID {
    NSInteger index = NSNotFound;
    NSInteger resIndex = 0;
    for (id<SHRMAppEventModuleProtocol>module in self.appEventModules)
    {
        if ([[module moduleID] isEqualToString:moduleID])
        {
            index = resIndex;
            break;
        }
        resIndex++;
    }
    
    if (index != NSNotFound) {
        [self.appEventModules removeObjectAtIndex:index];
    }
}
复制代码

总结

最后总结一下,关于模块化,现在总体来看比较流行,也有很多介绍模块化,组件化具体实施之路的文章,都很优秀,也值得学习。关于解耦,我更倾向于 protocol 的方式,接口 protocol 化,代码易读且清晰。之前看过 mrpeak 在组件化方面的文章,传送门 iOS 组件化方案 ,个人觉得 protocol+version 的方案和 BeeHive 非常像, protocol 解耦, version 进行 module 的版本管理,但是还是没有解决 module 所带来的内存开销问题, module 一旦细化,何时销毁也是让开发者头疼的问题。先说这么多,各位小伙伴有任何问题欢迎评论区讨论。

最后附上传送门: 《SHRMAppDelegate》


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK