AppDelegate的模块化+瘦身

前言

关于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?。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275919.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

yii mysql_Yii2框架操作数据库的方法分析【以mysql为例】

本文实例讲述了Yii2框架操作数据库的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;准备数据库DROP TABLE IF EXISTS pre_user;CREATE TABLE pre_user(id int(11) AUTO_INCREMENT PRIMARY KEY,username varchar(255) NOT NULL,password varchar(32) NOT NULL DEF…

C++接口注意

1. 用Record接口&#xff0c;要注意 Packed的区别 2. cdecl和stdcall的区别 3. C导出的函数建议用C格式stdcall导出&#xff0c;使用Def文件定义名称 4. 用VS写的API dll要注意是否引用了MFC的DLL&#xff0c;否则会使LoadLibrary失败&#xff0c;并GetLastError后返回14001 Ap…

Vue 3.1.0 的 beta 版发布

大家好&#xff0c;我是若川&#xff08;点这里加我微信 ruochuan12&#xff0c;长期交流学习&#xff09;。昨晚尤大视频号直播说到vue 3.1.0 beta版发布了&#xff0c;今天分享这篇文章。也有小伙伴可能注意到了昨晚我一直在送礼物。点击下方卡片关注我、加个星标&#xff0c…

设计模式练习_设计练习是邪恶的

设计模式练习It was the final round of interviews. Or, so the candidate thought.这是采访的最后一轮。 或者&#xff0c;所以候选人认为。 She’d spent all day interviewing in our office. As the final interviewer, I walked her out the building. She seemed confi…

morningcat2018 LearningDocs

2019独角兽企业重金招聘Python工程师标准>>> LearningDocs 学习资料与文档 JCP&#xff08;Java Community Process &#xff0c;Java社区进程 &#xff09; https://www.jcp.org/en/home/index JSR&#xff08;Java Specification Requests&#xff0c;Java规范请求…

firefox下可恶的value

前几天做项目 遇到这样一个情况 document.getElementById("txtTest").value "111"; 这条语句在ff下和ie下都是好用的 但是用开发工具看html代码 ie下显示正常 ff下显示的却是修改之前的(实际上已经修改了&#xff0c;只是html没有修改过来) 用js修改input…

据说 99% 的人不知道 vue-devtools 还能直接打开对应组件文件?本文原理揭秘

1. 前言你好&#xff0c;我是若川[1]&#xff0c;微信搜索「若川视野」关注我&#xff0c;专注前端技术分享&#xff0c;一个愿景是帮助5年内前端开阔视野走向前列的公众号。欢迎加我微信ruochuan12&#xff0c;长期交流学习。这是学习源码整体架构系列 之 launch-editor 源码&…

mysql 存储 事务_MYSQL 可以在存储过程里实现事务控制吗

展开全部6.7 MySQL 事务与锁定命令6.7.1 BEGIN/COMMIT/ROLLBACK 句法缺省的&#xff0c;MySQL 运行在 autocommit 模式。这就意味着&#xff0c;当你执行完一e69da5e887aa62616964757a686964616f31333361326265个更新时&#xff0c;MySQL 将立刻将更新存储到磁盘上。如果你使用…

如何忽略证书继续访问_前5个最容易被忽视的可访问性问题

如何忽略证书继续访问Accessibility is quickly becoming one of the most important aspects of the way we use the web, if not the most important. Just between 2017 and 2018, the number of federal court cases regarding web accessibility nearly tripled, signifyi…

《认清C++语言》のrandom_shuffle()和transform()算法

1&#xff09;STL中的函数random_shuffle()用来对一个元素序列进行重新排序&#xff08;随机的&#xff09;&#xff0c;函数原型如下&#xff1a; template<class RandomAccessIterator> void random_shuffle( RandomAccessIterator _First, //指向序列首元素的迭代器 R…

作为前端开发,如何高效学习 TypeScript

大家好&#xff0c;我是若川。有朋友跟我说最近面试前端候选人&#xff0c;问到关于 JavaScript 的一些少见误区问题&#xff0c;候选人很多都没回答上来&#xff0c;他很诧异&#xff0c;一个从国际大厂出来的面试者&#xff0c;竟然对 JavaScript 的一些误区问题都不了解。他…

figma下载_对于这10家公司,Figma是迈向新高度的起点

figma下载Hey everyone! In this post, we are highlighting 10 companies for which the use of Figma has become the starting point on the path to new heights. These are the use cases of problems and their solutions, where Figma played a decisive role.嘿大家&am…

mysql查询条件为or_使用mysql查询where条件里的or和and

为什么要着重讲这块内容呢?因为好多小伙伴都会混淆&#xff0c;要不就是不知道怎么组合使用&#xff0c;今天就给大家讲这部分内容干货&#xff0c;让大家半分钟看懂。AND、OR运算符的组合使用在WHERE子句中&#xff0c;通过AND、OR运算符可以同时连接多个条件&#xff0c;当然…

sql server(常用)

普通用法 //生成 uuid 并转为小写 select LOWER(SUBSTRING(uuid,1,8)-SUBSTRING(uuid,10,4)-SUBSTRING(uuid,15,4)-SUBSTRING(uuid,20,4)-SUBSTRING(uuid,25,12)) from (select cast(NEWID() as varchar(36)) as uuid) s //ea52a7bb-a2aa-44b8-be28-5ebc64defcf9//获取时分秒…

代码编写中会遇到的安全性问题

一、常用的攻击手段 1&#xff0e;脚本注入 漏洞描述&#xff1a; 脚本注入攻击在通过浏览器使用用户输入框插入恶意标记或脚本代码时发生。 如&#xff1a;某个输入框允许用户向数据存储中插入内容&#xff0c;如果将一段js脚本插入其中&#xff0c;则当其他用户使用或浏览此数…

TypeScript 原来可以这么香?!

先问一个问题&#xff0c;JavaScript有几种数据类型&#xff1f;number、string、boolean、null、undefined、symbol、bigint、object其中 bigint 是 ES2020 新增的数据类型&#xff0c;而早在 TS3.2 时便成为 TS 的标准&#xff0c;其实还有好多 ES 标准是 TS 率先提出的&…

java8新特性stream深入解析

2019独角兽企业重金招聘Python工程师标准>>> 继续java8源码的发烧热&#xff0c;越看越是有充实的感觉。 数据时代下的产物 Java顺应时代的发展推出的高效处理大量数据能力的api&#xff0c;它专注于对集合对象进行各种非常便利、高效的聚合操作&#xff0c;借助于同…

mysql内连接的自连接_mysql 内连接、外连接、自连接

一)内连接(等值连接)&#xff1a;查询客户姓名&#xff0c;订单编号&#xff0c;订单价格---------------------------------------------------select c.name,o.isbn,o.pricefrom customers c inner join orders owhere c.id o.customers_id;-------------------------------…

关于ASP.NET MVC

我是否要学习一下ASP.NET MVC呢&#xff1f;因爲从它刚发布的时候就已经初步的学习了一下&#xff0c;可是一直没有坚持下来。不过心里对于这份惦记&#xff0c;又让我始终放不下&#xff0c;看来应该抽个时间来系统的学习一下。 就这样吧&#xff0c;把自己的博客当成微博来使…

版式设计与创意 pdf_恋爱与版式

版式设计与创意 pdfSince its beginnings, Libération has been characterized by a very distinctive use of typeface, to such an extent that Libé has put its mark on fonts from across different eras, appropriating these in a certain way.小号因斯它的起点&…