前言
关于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:sel
withObject:application
withObject:launchOptions];
#pragma clang diagnostic pop
}];
return YES;
}
没看错,这样一搞AppDelegate
的application: 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])
就会执行complete
将module
和sel
返回,也就是到了AppDelegate
里面,继而执行module
的sel
函数,并且将参数传递过去。
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
一旦细化,何时销毁也是让开发者头疼的问题。先说这么多,各位小伙伴有任何问题欢迎评论区讨论。
git地址:AppDelegateMudule,欢迎star🎉。
网友评论