前言

关于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中进行处理的业务逻辑,比如说我们的数据库处理分享功能推送功能等等。

首先为所有模块定义了三个接口:

@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:selwithObject:applicationwithObject: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)eventSelComplete:(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)eventSelComplete:(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一旦细化,何时销毁也是让开发者头疼的问题。先说这么多,各位小伙伴有任何问题欢迎评论区讨论。

最后附上DEMO传送门:AppDelegateMudule,欢迎star?。

AppDelegate的模块化+瘦身相关推荐

  1. AppDelegate瘦身指南

    原文链接:http://kyson.cn/index.php/archives/105/ AppDelegate瘦身是一个大家都很熟悉的话题,各家也有各自的解决方案.但方案无外乎两种,一种是从AppD ...

  2. 基于clang插件的一种iOS包大小瘦身方案

    引子 \ 包瘦身,包瘦身,包瘦身,重要的事情说三遍. \ 最近公司一款iOS APP(本文只讨论使用Objective C开发的iOS安装包)一直在瘦身,我们团队的APP也愈发庞大了.而要解决这个问题 ...

  3. newduba首页怎么去掉_京喜小程序首页瘦身实践

    前言 在 web 开发场景,减少代码体积虽然是性能优化的一个方向,还没到锱铢必较的程度.但是在小程序场景,由于代码包上传阶段限制了主包 2M 和总包 16M(近期微信官方正在内测将总包上限调整至 20 ...

  4. iOS应用程序瘦身的静态库解决方案

    为什么要给程序瘦身? 随着应用程序的功能越来越多,实现越来越复杂,第三方库的引入,UI体验的优化等众多因素程序中的代码量成倍的增长,从而导致应用程序包的体积越来越大.当程序体积变大后不仅会出现编译流程 ...

  5. 京东商城iOS客户端安装包瘦身实践

    一.概述 随着业务的快速增加,商城app的大小也在迅速增加,一度超过了300M.安装包大小的不断增加对app下载成本,推广效率产生了比较大的影响.从2018年9月份我们对商城app开始了为期二期的专项 ...

  6. JDK14+JAVAFX14+Maven定制jre打包瘦身,必成版

    注: 本教程jdk9以上版本通用(任何java项目都可以通过本教程精简jre) 博客来由: jdk9以后代码模块化逐渐成为趋势,jlink工具开始出现在人们的视野中,它可以用来定制项目所需要的jre, ...

  7. iOS安装包瘦身的那些事儿

    在我们提交安装包到App Store的时候,如果安装包过大,有可能会收到类似如下内容的一封邮件: 收到这封邮件的时候,意味着安装包在App Store上下载的时候,有的设备下载的安装包大小会超过100 ...

  8. [MaxCompute MapReduce实践]通过简单瘦身,解决Dataworks 10M文件限制问题

    用户在DataWorks上执行MapReduce作业的时候,文件大于10M的JAR和资源文件不能上传到Dataworks,导致无法使用调度去定期执行MapReduce作业. 解决方案: 第一步:大于1 ...

  9. SpringBoot 部署 Jar 文件,瘦身优化指南 !

    以下文章来源方志朋的博客,回复"666"获面试宝典 作者 | Java基基 来源 | https://mp.weixin.qq.com/s/Y5VK7TI1TQBN6O-k5O6h ...

最新文章

  1. Python3.5模块‘OS’‘sys’
  2. insert执行时oracle如何处理,ORACLE中Insert时字符处理
  3. Eclipse旧版本Luna SR2(版本4.4.2)下载地址
  4. Spring Boot 整合 Netty(附源码)
  5. 《他其实没那么喜欢你》经典台词
  6. [Pytorch].pth转.pt文件
  7. linux windows主题下载官网,Linux Deepin 15.10.2 桌面kwin主题App美化
  8. 消息发送和接收基本应用
  9. 视频预训练界的HERO!微软提出视频-语言全表示预训练模型HERO,代码已开源!...
  10. 工厂支持多数据库开发的三层结构模式随笔(一)
  11. BZOJ 2288: 【POJ Challenge】生日礼物 优先队列+贪心+链表
  12. mysql数字辅助表_关于数字的经典SQL编程问题:数字辅助表
  13. 如何实现音频合成立体声录制?
  14. 博途PLC和ABB变频器PN通讯详解
  15. NET Framework 4 与.NET Framework 4 Client Profile有什么区别?
  16. mysql drop表明_MySQL DROP TABLE会完全删除表还是仅删除结构?
  17. manjaro上安装独显驱动(双显卡切换)的正确方法
  18. 加州房价篇 (三) : 模型的训练,评估和房价的预测
  19. Android网卡网速测试
  20. Facebook 申请蓝色认徽章

热门文章

  1. 中文格式_常见中文编码格式
  2. 命令点亮硬盘灯_macOS下移动硬盘无法挂载且硬盘灯一直闪烁的解决方法
  3. easy-ui的datagrid
  4. 列表和range、元组
  5. 《梦断代码》阅读笔记02
  6. codeforces 650B - Image Preview
  7. cocos2d-x游戏开发 跑酷(四) 关联与物理世界
  8. 几种常用控件的使用方法
  9. sql 写query_为什么需要动态SQL
  10. 从文件中读出数据显示在表格中_玩转表格:如何在Word表格中进行数据计算?...