阿里的BeeHive框架初探

好久没有写文章了,感觉自己快要生锈了,最近忙完公司的新项目。趁下一个版本还没有出来,抽空看看有什么新的框架,学习一波。
看看现在的MVC、MVP、MVVM等设计模式,无非就是为了解耦、组件化等优点。项目一大,各种跨模块跳转就变得很平常了,所以有时候想抽一个模块出来,但是一动牵扯全身,连测试一个小功能都要运行整个项目。
其实我关注阿里的BeeHive也有一段时间了,也第一时间运行了demo,发现功力不够,理解不了,可能是当时急躁吧。现在想着闲着,为什么不挑战一下呢。


我结合官方文档以及其他作者写的文章看看框架的原理,可能理解的不对或者写的不好,如果有错误欢迎更正,毕竟还是小白,一起共勉吧。
至于对框架的陈述,看官方文档就可以了,这里不再累述。


一个非常重要的提示:我写的非常啰嗦,逻辑经常跳转,只做自己学习笔记


运行demo

下载工程->打开终端->输入cd 工程地址->pod install->打开xcworkspace工程->运行。
看到主界面,当然是随便点击,能点击的都点击。

image

发现没有什么实际的功能展示。只有4个tab和两个页面有一个按钮点击,做跳转页面。
只能看代码了。

查看目录结构

image

查看TestAppDelegate

我觉得看一个程序,看主入口是最好的。可以知道框架实现的第一步。
在application:didFinishLaunchingWithOptions:方法中看到以下代码

1
2
3
4
5
6
7
8
[BHContext shareInstance].application = application;
[BHContext shareInstance].launchOptions = launchOptions;
[BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默认为BeeHive.bundle/BeeHive.plist
[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";

[BeeHive shareInstance].enableException = YES;
[[BeeHive shareInstance] setContext:[BHContext shareInstance]];
[[BHTimeProfiler sharedTimeProfiler] recordEventTime:@"BeeHive::super start launch"];

从以上代码初步得到BHContext是一个管理整个应用级别的上下文类。以至于要赋值application和launchOptions给BHContext。由于BHContext是使用单例,所以整个应用期间获取的都是同一个对象。
moduleConfigName和serviceConfigName分别对应的是模块配置的文件名和服务协议配置的文件名。这两个文件已经在框架里面了,不需要再次创建。这个两个文件有什么作用呢,这边先提示一下,做静态注册的。也就是访问plist文件获取模块数组、协议数组数据,然后进行注册。当然也会有动态注册,后面会提到。

BeeHive暂且理解为框架使用的主要类
enableException是否启动异常,用来捕捉异常错误的
BHTimeProfiler记录事件

继续往下看。。。


1
id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

看到代码,第一感觉是要创建home控制器,但是为什么这样创建呢?以前看过依赖注入,感觉有点相似。可以大概理解为:利用BeeHive绑定HomeServiceProtocol协议,从而获取实现HomeServiceProtocol协议的home控制器。也就是说要具备一下条件:

  1. 创建HomeServiceProtocol协议
  2. home控制器必须实现HomeServiceProtocol协议
  3. BeeHive注册(HomeServiceProtocol协议和实现HomeServiceProtocol的homeVc)服务
  4. 通过createService:方法来获取homeVc

满足以上条件就可以创建home控制器。

1
2
3
4
5
6
7
8
if ([homeVc isKindOfClass:[UIViewController class]]) {
UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:(UIViewController*)homeVc];

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = navCtrl;

[self.window makeKeyAndVisible];
}

通过这段代码,证明我是正确的,前面的代码确实是要创建home控制器。

到此TestAppDelegate的代码已看完。那么就要进行第二步了,第二部怎么走呢?不是提高homeVc吗?那就从homeVc入手。


看homeVc=BHViewController

BeeHiveService宏定义

1
@BeeHiveService(HomeServiceProtocol,BHViewController)

进入里面看到

1
2
#define BeeHiveService(servicename,impl) \
class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}";

也就是说这个宏定义是实现API注册的。对服务和需要实现这个服务的类进行绑定注册。
那么,在代码中和官网文档中找到:

1
[[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];

那么这个宏定义就不难理解了


需要实现HomeServiceProtocol

1
@interface BHViewController ()<HomeServiceProtocol>

进入HomeServiceProtocol里面查看,发现协议除了实现NSObject协议之外还要实现框架的基础BHServiceProtocol协议

而在HomeServiceProtocol中,有一个创建控制器的方法。那么这里就是添加一些业务需要的方法了。

我尝试添加一个方法:

1
- (void)aFunctionForTest;

BHViewController文件中就有一个警告,叫我要实现aFunctionForTest方法,因为我协议没有添加@optional,默认是必须实现的。

接着继续看->

init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-(instancetype)init {
self = [super init];
if (self) {
self.registerViewControllers = [[NSMutableArray alloc] initWithCapacity:1];

demoTableViewController *v1 = [[demoTableViewController alloc] init];
if ([v1 isKindOfClass:[UIViewController class]]) {
[self registerViewController:v1 title:@"埋点" iconName:nil];
}

id<UserTrackServiceProtocol> v4 = [[BeeHive shareInstance] createService:@protocol(UserTrackServiceProtocol)];
if ([v4 isKindOfClass:[UIViewController class]]) {
[self registerViewController:(UIViewController *)v4 title:@"埋点3" iconName:nil];
}

id<TradeServiceProtocol> v2 = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];
if ([v2 isKindOfClass:[UIViewController class]]) {
v2.itemId = @"sdfsdfsfasf";
[self registerViewController:(UIViewController *)v2 title:@"交易2" iconName:nil];
}

id<TradeServiceProtocol> s2 = (id<TradeServiceProtocol>)[[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];
if ([s2 isKindOfClass:[UIViewController class]]) {
s2.itemId = @"例子222222";
[self registerViewController:(UIViewController *)s2 title:@"交易3" iconName:nil];
}
}
return self;
}

从代码上看,是要创建四个控制器,那么作者已经做了一个对比。第一个v1创建方式就是使用传统的方式,需导入demoTableViewController的头文件。而其他三个控制器v4、v2、s2则是通服务来获取,不需要导入这几个控制器的头文件。那么就达到解耦的第一步。当然协议的头文件还是要导入。创建的协议文件的导入生命全部放在BeeHive.h里面了。

其他方法

1
2
3
4
5
6
7
8
9
-(void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName
{
vc.tabBarItem.image = [UIImage imageNamed:[NSString stringWithFormat:@"Home.bundle/%@", iconName]];
vc.tabBarItem.title = title;

[self.registerViewControllers addObject:vc];

self.viewControllers = self.registerViewControllers;
}

这个方法是把创建的代码全部放在一起,传递不同的参数即可。用数组保存4个控制器。看到这里,我发现HomeServiceProtocol里面也有这个方法,是不是有什么关联呢?

实践:我把协议的registerViewController: title: iconName:注释掉,运行程序,照样运行。所以暂时没发现作用。要留到最后看看才知道了😆😆😆

1
2
3
4
5
6
7
- (void)click:(UIButton *)btn {
id<TradeServiceProtocol> obj = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];
if ([obj isKindOfClass:[UIViewController class]]) {
obj.itemId = @"12313231231";
[self.navigationController pushViewController:(UIViewController *)obj animated:YES];
}
}

我在这个方法里面打个断点,试着把程序的点击了,然而没有进入,所以程序的点击跟这个方法是没有关系的。不知道作者写这个方法是用在什么地方,只能后面查看到才说明,断点会一直保留的。

到此,homeVc基本查看完了。那么就要接续下一步了。这边有几个提示:

  1. 提到的基础服务:BHServiceProtocol
  2. 用户跟踪服务UserTrackServiceProtocol
  3. 贸易服务TradeServiceProtocol

既然跟服务都有关系的,基础服务就显得额外重要了,就从BHServiceProtocol入手。

BHServiceProtocol

1
2
3
4
5
@optional

+ (BOOL)singleton;

+ (id)shareInstance;

从方法名称上看,第一是是否需要单例,第二个是单例实现声明。
官方文档解释的是:

1
2
3
+ (BOOL)singleton{
return YES;
}

如果返回时YES,那么获取的对象就是单例的。相反就是多例的。
我把singleton进行全局收索,发现BHServiceManager类中createService:方法有写到singleton,代码如下:

为了更好的理解,我就在代码中添加注释来说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/// 创建服务
- (id)createService:(Protocol *)service
{
id implInstance = nil;

// 如果没有包含该服务,抛出一个异常。异常也说明了,这个服务没有注册
if (![self checkValidService:service]) {
if (self.enableException) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol does not been registed", NSStringFromProtocol(service)] userInfo:nil];
}

}

// 获取服务名称
NSString *serviceStr = NSStringFromProtocol(service);
// 获取该服务的实例,如果有直接返回
id protocolImpl = [[BHContext shareInstance] getServiceInstanceFromServiceName:serviceStr];
if (protocolImpl) {
return protocolImpl;
}

// 获取服务对应的类
Class implClass = [self serviceImplClass:service];
// 判断类是否实现了singleton方法
if ([[implClass class] respondsToSelector:@selector(singleton)]) {
// 调用singleton方法,获取BOOL值,YES就是需要实现单例了
if ([[implClass class] singleton]) {
// 判断类是否显示shareInstance方法
if ([[implClass class] respondsToSelector:@selector(shareInstance)])
// 创建单例
implInstance = [[implClass class] shareInstance];
else
// 普通创建,多例
implInstance = [[implClass alloc] init];

// 保存到上下文中
[[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
return implInstance;
}
}

// 没有实现singleton方法,直接创建
return [[implClass alloc] init];
}

从代码上看出,要实现该类是单例的话,需要实现两个方法。

    • (BOOL)singleton;
    • (id)shareInstance;

那么,基础服务已经看完。
接着,就按照顺序,把UserTrackServiceProtocol也看了


UserTrackServiceProtocol

😆😆,里面没东西看。找对应的BHUserTrackViewController

BHUserTrackViewController

1
2
3
4
5
6
7
8
// 注册服务
@BeeHiveService(UserTrackServiceProtocol,BHUserTrackViewController)

// 该类不是单例
+(BOOL)singleton
{
return NO;
}

没了,那么继续下一个TradeServiceProtocol

TradeServiceProtocol

1
2
// 声明一个属性
@property(nonatomic, strong) NSString *itemId;

BHTradeViewController

打开看看,里面有代码😎😎。
突然看到没有使用@BeeHiveService进行注册?黑人问号???
他是怎么做到的?刚才createService:方法已经说明了,如果没有注册会抛出一个异常,然而并没有,那就很奇怪了。
吓得我赶紧把TradeServiceProtocol全局搜索了一遍,获取了一个关键信息,那就是在TradeModule类中找到了注册TradeServiceProtocol的代码

1
2
3
4
5
6
- (void)modSetUp:(BHContext *)context
{
[[BeeHive shareInstance] registerService:@protocol(TradeServiceProtocol) service:[BHTradeViewController class]];

NSLog(@"TradeModule setup");
}

这时就猜想了,这个TradeModuleBHTradeViewController肯定有一腿。或者还跟TradeServiceProtocol搞三角恋。至于他们是什么关系,这里暂不解析,后面再分析。

好了,回归到原点,在BHTradeViewController中添加了UILabel,UIButton控件,并且点击跳转到相应页面。并且传递参数itemId。
也就是说如果A跳转到B,需要传递数据给B,那么在B的服务上声明属性或者方法即可。至于回调数据使用block、delegate、NSNotification方式即可。

比如我在TradeServiceProtocol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 声明一个block
typedef void (^CallBackBlock)(NSString *string);

// 声明一个协议
@protocol TradeViewControllerProtocol <NSObject>
- (void)tradeViewControllerDidClickBtn;
@end

@protocol TradeServiceProtocol <NSObject, BHServiceProtocol>


@property(nonatomic, strong) NSString *itemId;

// block回调属性
@property (nonatomic, copy) CallBackBlock callBackBlock;

// 代理
@property (nonatomic, weak) id<TradeViewControllerProtocol> delegate;

@end

那么在A那里就可以设置相关属性,即可获取到回调的数据啦。

到这里已经把相关服务和类看完了。然而因为查看BHTradeViewController,让我又发现一个新的知识,那就是TradeModule,接下来就是分析TradeModule了。


TradeModule

从目录中,不难发现每个模块都有一个类是对应Modelue。

  • 贸易:TradeModule
  • 首页:HomeModule
  • 用户跟踪:UserTrackModule
  • 商铺:ShopModule

实现BHModuleProtocol

首先看到类TradeModule需要实现BHModuleProtocol协议,这里就会有两个疑问?

  • 为什么要实现这个协议?
  • 这个协议的作用是什么?

先保留着这些疑问继续看代码。

load方法

1
2
3
4
+ (void)load
{
[BeeHive registerDynamicModule:[self class]];
}

load的方法相比很少人注意到。可能平时开发也很好用到。load方法是什么意思?又是什么时候调用的呢?这里拓展一下传送门
其实归纳一句话就是主程序main运行时,就会调用load方法。

registerDynamicModule:

也就是说,程序一开始就进行了注册,那么registerDynamicModule:的实现是怎样的呢?
分析:
从方法的名称可以的到是对动态模块进行注册,而并不是像之前提到的是registerService:对服务进行注册。那么就会有一个疑问,提到动态,是不是也会有静态模块注册呢?是的,一开始文章提到的两个plist文件,就是做静态注册的,在查看TestAppDelegate那里。

一层层进入代码,最后到达addModuleFromObject: shouldTriggerInitEvent:方法里面,那就分析代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
添加模块

@param object 模块对象
@param shouldTriggerInitEvent 是否要触发初始化事件
*/

- (void)addModuleFromObject:(id)object
shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
Class class;
NSString *moduleName = nil;

// 获取模块对象的类名
if (object) {
class = object;
moduleName = NSStringFromClass(class);
} else {
return ;
}

// 遍历BHModules数组,判断是否已经添加过了,有的话直接return掉。
__block BOOL flag = YES;
[self.BHModules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:class]) {
flag = NO;
*stop = YES;
}
}];
if (!flag) {
return;
}

// 判断模块类是否显示BHModuleProtocol协议。(到这里就可以回答文章上面为什么要实现这个协议?的疑问,你不实现协议没法进入if里面继续执行啊)
if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {
NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];

// 判断类是否包含basicModuleLevel方法
BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];

int levelInt = 1;
// 有包含,级别为0
if (responseBasicLevel) {
levelInt = 0;
}

// 把级别、类名保存到字典里
[moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
if (moduleName) {
[moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
}

// 添加到模块信息数组里
[self.BHModuleInfos addObject:moduleInfo];

//初始化模块对象
id<BHModuleProtocol> moduleInstance = [[class alloc] init];
// 添加到模块数组里
[self.BHModules addObject:moduleInstance];
[moduleInfo setObject:@(YES) forKey:kModuleInfoHasInstantiatedKey];

// 根据级别对模块数组的模块对象进行排序
[self.BHModules sortUsingComparator:^NSComparisonResult(id<BHModuleProtocol> moduleInstance1, id<BHModuleProtocol> moduleInstance2) {
// 默认两个对象级别是1
NSNumber *module1Level = @(BHModuleNormal);
NSNumber *module2Level = @(BHModuleNormal);
// 判断对象是否实现basicModuleLevel,有的话级别是0
if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) {
module1Level = @(BHModuleBasic);
}
if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) {
module2Level = @(BHModuleBasic);
}
if (module1Level.integerValue != module2Level.integerValue) {
return module1Level.integerValue > module2Level.integerValue;
} else {
// 默认两个对象的优先级是0
NSInteger module1Priority = 0;
NSInteger module2Priority = 0;
// 判断对象是否实现modulePriority方法,获取优先级数,数越大优先级越高
if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) {
module1Priority = [moduleInstance1 modulePriority];
}
if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) {
module2Priority = [moduleInstance2 modulePriority];
}
return module1Priority < module2Priority;
}
}];
// 根据对象注册事件
[self registerEventsByModuleInstance:moduleInstance];

// 如果开启触发事件
if (shouldTriggerInitEvent) {
// 处理BHMSetupEvent事件
[self handleModuleEvent:BHMSetupEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
// 处理BHMInitEvent事件
[self handleModulesInitEventForTarget:moduleInstance withCustomParam:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// 处理BHMSplashEvent事件
[self handleModuleEvent:BHMSplashEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
});
}
}
}

在这方法,我先不深究里面的子方法,不然绕不回来的。总的来说,确实证明之前的疑问:

  1. registerDynamicModule:方法是对模块类进行注册的
  2. 模块类TradeModule需要实现BHModuleProtocol协议
  3. 为什么实现BHModuleProtocol协议,因为协议里有很多方法,暂时接触到有basicModuleLevel、modulePriority两个方法,所以需要实现协议的方法来传递一些数据。

现在继续查看TradeModule的代码。

modInit:方法

1
2
3
4
5
6
7
8
9
10
-(void)modInit:(BHContext *)context
{
NSLog(@"模块初始化中");
NSLog(@"%@",context.moduleConfigName);


id<TradeServiceProtocol> service = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];

service.itemId = @"我是单例";
}

这个方式是模块初始化,什么时候调用呢?全局搜索一下找到信息:

  1. 找到static NSString *kSetupSelector = @”modSetUp:”;
  2. 搜索kSetupSelector,找到BHMSetupEvent
  3. 搜索BHMSetupEvent,找到刚才注册模块里的代码,其中里面的
1
2
// 处理BHMSetupEvent事件
[self handleModuleEvent:BHMSetupEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];

也就是说load方法执行的时候,就执行modInit:方法了,那么根据线索:

1
2
3
4
5
6
7
8
9
10
11
// 如果开启触发事件
if (shouldTriggerInitEvent) {
// 处理BHMSetupEvent事件
[self handleModuleEvent:BHMSetupEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
// 处理BHMInitEvent事件
[self handleModulesInitEventForTarget:moduleInstance withCustomParam:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// 处理BHMSplashEvent事件
[self handleModuleEvent:BHMSplashEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
});
}

就可以判断协议的几个方法:

1
2
3
4
5
- (void)modSetUp:(BHContext *)context;

- (void)modInit:(BHContext *)context;

- (void)modSplash:(BHContext *)context;

在load执行的时候就执行了。而且顺序分别是:modSetUp:->modInit:->modSplash:
注意:modSplash:是异步执行的

使用场景:modSetUp:可以对模块里的所有服务进行注册。modInit:对服务进行相关赋值需要的数据。modSplash:暂时不清楚如何使用

到此TradeModule分析完了吗?算是吧,另外的方法

1
2
3
4
5
6
- (void)modSetUp:(BHContext *)context
{
[[BeeHive shareInstance] registerService:@protocol(TradeServiceProtocol) service:[BHTradeViewController class]];

NSLog(@"TradeModule setup");
}

上面已经分析的。但是呢..😎😎

有句话我很好奇:service.itemId = @"我是单例";

1
2
id<TradeServiceProtocol> service = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];
service.itemId = @"我是单例";

我第一印象是service是单例吗?之前不是说要实现

1
2
3
4
5
@optional

+ (BOOL)singleton;

+ (id)shareInstance;

这些代码才是单例吗?TradeModule没有这些代码啊,难道是其他地方实现了而且我不知道的?带着疑问,我把获取TradeServiceProtocol服务相关的搜索了一遍。然后把他们的获取的对象全部打印一下地址,如果是一样的,那就是单例,实现方法还不知道。如果是不一样,那可能是作者笔误了。
打印:

1
2
<BHTradeViewController: 0x7fd53cd0f7d0>
<BHTradeViewController: 0x7fd53cc3c610>

确实是不一样,难道真是作者笔误了?然而你们也被我的印象误导了,当然这是一个好的猜想,也验证了猜想是错误的。
或者进入之前提到的createService:方法里面,看看是否进入以下代码就知道是否是单例了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 判断类是否实现了singleton方法
if ([[implClass class] respondsToSelector:@selector(singleton)]) {
// 调用singleton方法,获取BOOL值,YES就是需要实现单例了
if ([[implClass class] singleton]) {
// 判断类是否显示shareInstance方法
if ([[implClass class] respondsToSelector:@selector(shareInstance)])
// 创建单例
implInstance = [[implClass class] shareInstance];
else
// 普通创建,多例
implInstance = [[implClass alloc] init];

// 保存到上下文中
[[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
return implInstance;
}
}

service.itemId = @"我是单例" 只是简单的传值罢了,可能这个值让人有点误会,如果改成 service.itemId = @"我是参数" 那就很好理解了。

到这里把上面的问题解答一下:之前说TradeModule、TradeServiceProtocol、BHTradeViewController是不是搞三角关系?他们的关系是这样的:BHTradeViewController需要实现TradeServiceProtocol,TradeServiceProtocol是BHTradeViewController对外的接口。而TradeModule只是相当于一个容器,把他们放在自己这里而已,把这个模块的所有服务啊,进行注册的控制器全部放在这里,统一进行管理罢了。

TradeModule基本分析完了,那么既然看完了这个模块的,顺便把之前提高的其他模块的模块类也看了,互相对比一下


HomeModule

1、基本跟TradeModule一样,需要实现==BHModuleProtocol==基础协议。

2、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 在初始化方法中,BHContext的env属性是枚举类型,里面有:
BHEnvironmentDev = 0, // 开发环境
BHEnvironmentTest, // 测试环境
BHEnvironmentStage, // 阶段环境
BHEnvironmentProd // 生产环境
// 对模块或者整个程序进行不同环境的切换。
-(void)modInit:(BHContext *)context
{
switch (context.env) {
case BHEnvironmentDev:
//....初始化开发环境
break;
case BHEnvironmentProd:
//....初始化生产环境
default:
break;
}
}
// 由于BHContext是单例,可以在TestAppDelegate中添加:
[BHContext shareInstance].env = BHEnvironmentTest;
// 那么整个程序都处于一个测试的阶段了。

UserTrackModule

1、需要实现BHModuleProtocol基础协议。

2、BH_EXPORT_MODULE(NO)
BH_EXPORT_MODULE是个宏定义,可以进入里面

1
2
3
#define BH_EXPORT_MODULE(isAsync) \
+ (void)load { [BeeHive registerDynamicModule:[self class]]; } \
-(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];}

也就是说是动态注册,实际实现两个方法(+(void)load-(BOOL)async) 。当然也可以使用静态注册:通过BeeHive.plist文件中注册符合BHModuleProtocol协议的模块类
BOOL 该模块是否是异步加载,在启动之后第一屏内容展示之前异步执行模块的初始化方法。在想如果不是每个tab的模块,其他子模块暂时不需要的,基本需要异步加载,优化启动时间。

ShopModule

1、需要实现BHModuleProtocol基础协议。

2、@BeeHiveMod(ShopModule)
这个宏定义实际跟之前的不一样,我也不是很能理解,这里有一个大神的文章可以借鉴一下BeeHive——一个优雅但还在完善中的解耦框架

里面有提到@BeeHiveMod这个宏是怎么运作的,是Annotation方式注册。其他也提到的方式读取plist文件=静态注册,load方式=动态注册。

BHModuleProtocol

终于到BHModuleProtocol了,二话不说看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//如果不去设置Level默认是Normal
//basicModuleLevel不去实现默认Normal
- (void)basicModuleLevel;
//越大越优先
- (NSInteger)modulePriority;

/// 返回YES,异步注册
- (BOOL)async;
/// 模块设置:可用于模块内各个类注册使用
- (void)modSetUp:(BHContext *)context;
/// 模块初始化:可用于模块内各个类简单赋值数据(特殊)
- (void)modInit:(BHContext *)context;
/// 闪屏
- (void)modSplash:(BHContext *)context;
/// 重击事件,6s以上设备支持的重击App Icon后唤起的3DTouch功能
- (void)modQuickAction:(BHContext *)context;
/// 模块拆除事件(特殊,module卸载的时候,但BHDelegate里面没实现调用
- (void)modTearDown:(BHContext *)context;

/// 以下是系统事件,可参考UIApplicationDelegate
- (void)modWillResignActive:(BHContext *)context;

- (void)modDidEnterBackground:(BHContext *)context;

- (void)modWillEnterForeground:(BHContext *)context;

- (void)modDidBecomeActive:(BHContext *)context;

- (void)modWillTerminate:(BHContext *)context;

- (void)modUnmount:(BHContext *)context;

- (void)modOpenURL:(BHContext *)context;

- (void)modDidReceiveMemoryWaring:(BHContext *)context;

- (void)modDidFailToRegisterForRemoteNotifications:(BHContext *)context;

- (void)modDidRegisterForRemoteNotifications:(BHContext *)context;

- (void)modDidReceiveRemoteNotification:(BHContext *)context;

- (void)modDidReceiveLocalNotification:(BHContext *)context;

- (void)modWillPresentNotification:(BHContext *)context;

- (void)modDidReceiveNotificationResponse:(BHContext *)context;

- (void)modWillContinueUserActivity:(BHContext *)context;

- (void)modContinueUserActivity:(BHContext *)context;

- (void)modDidFailToContinueUserActivity:(BHContext *)context;

- (void)modDidUpdateContinueUserActivity:(BHContext *)context;

- (void)modHandleWatchKitExtensionRequest:(BHContext *)context;

/// 用户自定义事件,BHModuleEventType > 1000
- (void)modDidCustomEvent:(BHContext *)context;

事件绑定对应的枚举是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typedef NS_ENUM(NSInteger, BHModuleEventType)
{
BHMSetupEvent = 0,
BHMInitEvent,
BHMTearDownEvent,// 拆除事件,module卸载的时候,但BHDelegate里面没实现调用
BHMSplashEvent,//闪屏
BHMQuickActionEvent,// 重击事件,6s以上设备支持的重击App Icon后唤起的3DTouch功能
BHMWillResignActiveEvent,// 将要注册激活
BHMDidEnterBackgroundEvent,// 已进入后台
BHMWillEnterForegroundEvent,// 将要进入前台
BHMDidBecomeActiveEvent,// 程序激活
BHMWillTerminateEvent,// 将要结束
BHMUnmountEvent,//卸载
BHMOpenURLEvent,// 打开路径
BHMDidReceiveMemoryWarningEvent,// 接收到内存警告
BHMDidFailToRegisterForRemoteNotificationsEvent,// 注册远程通知失败
BHMDidRegisterForRemoteNotificationsEvent,//注册远程通知
BHMDidReceiveRemoteNotificationEvent,//接收远程通知
BHMDidReceiveLocalNotificationEvent,//接收本地通知
BHMWillPresentNotificationEvent,// 即将触发的推送
BHMDidReceiveNotificationResponseEvent,// 收到通知回调
BHMWillContinueUserActivityEvent,// 将要继续用户活动
BHMContinueUserActivityEvent,// 继续用户活动
BHMDidFailToContinueUserActivityEvent,// 用户持续活动失败
BHMDidUpdateUserActivityEvent,// 更新用户活动
BHMHandleWatchKitExtensionRequestEvent,// 处理WatchKit额外请求
BHMDidCustomEvent = 1000

// 注意:添加自定义事件的时候,请移步BHSelectorByEvent字典也要添加对应的元数
};

枚举事件类型主要分成三种:

  1. 系统事件
  2. 应用事件
  3. 自定义事件

至于怎么触发这些事件呢,其实前面已经把

  1. BHMSetupEvent = 0,
  2. BHMInitEvent,
  3. BHMSplashEvent

这三个事件触发已经说了。那么接下来就说说其他事件,从哪方面入手呢?既然是提到模块协议BHModuleProtocol,那想必也有一个对应的模块管理类,那就是BHModuleManager

BHModuleManager

BHModuleManager.h

头文件有两个枚举声明(BHModuleLevel, BHModuleEventType)BHModuleLevel表示模块的级别;BHModuleEventType表示对应事件的枚举元数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/// 单例
+ (instancetype)sharedManager;

// If you do not comply with set Level protocol, the default Normal
/// 注册动态模块,这个方法就之前就接触到了,只不过在外表在包装一层而已。我们在模块类load方法中看到[BeeHive registerDynamicModule:[self class]];其实底层是使用[[BHModuleManager sharedManager] registerDynamicModule:moduleClass];也就调用了此方法。
- (void)registerDynamicModule:(Class)moduleClass;

/// 注册动态模块,是否将要触发事件的初始化(使用方式跟上面的解释一样)
- (void)registerDynamicModule:(Class)moduleClass
shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent;

/// 取消注册动态模块,这个方法没有在BeeHive使用,那么自己也可以添加到BeeHive中,包装一层,或者使用BHModuleManager直接使用都是可以的
- (void)unRegisterDynamicModule:(Class)moduleClass;

/// 加载本地模块
- (void)loadLocalModules;

/// 注册本地所有模块
- (void)registedAllModules;


/**
注册自定义事件

@param eventType 事件类型
@param moduleInstance 模块对象
@param selectorStr 方法字符串
*/
- (void)registerCustomEvent:(NSInteger)eventType
withModuleInstance:(id)moduleInstance
andSelectorStr:(NSString *)selectorStr;


/**
触发事件

@param eventType 事件类型(如果是自定义事件eventType > 1000)
*/
- (void)triggerEvent:(NSInteger)eventType;


/**
触发事件

@param eventType 事件类型(如果是自定义事件eventType > 1000)
@param customParam 自定义参数
*/
- (void)triggerEvent:(NSInteger)eventType
withCustomParam:(NSDictionary *)customParam;

BHModuleManager.m

属性
1
2
3
4
5
6
7
8
9
10
11
12
13
@interface BHModuleManager()
/// 存放模块类数组
@property(nonatomic, strong) NSMutableArray *BHModuleDynamicClasses;
/// 存放模块信息数组
@property(nonatomic, strong) NSMutableArray<NSDictionary *> *BHModuleInfos;
/// 模块数组
@property(nonatomic, strong) NSMutableArray *BHModules;
/// 模块数组事件字典(key:事件枚举,value:模块数组)
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSMutableArray<id<BHModuleProtocol>> *> *BHModulesByEvent;
/// 事件字典(key:事件枚举,value:事件名称)
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSString *> *BHSelectorByEvent;

@end
方法

读取plist文件,加载本地模块loadLocalModules,必须按照指定的格式,如下图:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#pragma mark - public
/// 单例
+ (instancetype)sharedManager
{
static id sharedManager = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedManager = [[BHModuleManager alloc] init];
});
return sharedManager;
}

/// 读取plist文件,加载本地模块
- (void)loadLocalModules
{
// 获取模块配置名称,这个在TestAppDelegate中已经设置过了
// [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默认为BeeHive.bundle/BeeHive.plist
NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
return;
}

NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
// 获取模块类数组
NSArray<NSDictionary *> *modulesArray = [moduleList objectForKey:kModuleArrayKey];
NSMutableDictionary<NSString *, NSNumber *> *moduleInfoByClass = @{}.mutableCopy;
// 遍历模块信息数组
[self.BHModuleInfos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 将模块类字典以模块类为key,@1为值
[moduleInfoByClass setObject:@1 forKey:[obj objectForKey:kModuleInfoNameKey]];
}];
// 遍历模块类数组
[modulesArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 如果没有就添加到模块信息数组里
if (!moduleInfoByClass[[obj objectForKey:kModuleInfoNameKey]]) {
[self.BHModuleInfos addObject:obj];
}
}];
}

/**
注册动态模块
@param moduleClass 模块类
*/
- (void)registerDynamicModule:(Class)moduleClass
{
[self registerDynamicModule:moduleClass shouldTriggerInitEvent:NO];
}
/**
注册动态模块
@param moduleClass 模块类
@param shouldTriggerInitEvent 是否将要触发事件的初始化
*/
- (void)registerDynamicModule:(Class)moduleClass
shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
[self addModuleFromObject:moduleClass shouldTriggerInitEvent:shouldTriggerInitEvent];
}
/**
取消注册动态模块
@param moduleClass 模块类
*/
- (void)unRegisterDynamicModule:(Class)moduleClass {
if (!moduleClass) {
return;
}
// 过滤跟moduleClass不相等的模块信息
[self.BHModuleInfos filterUsingPredicate:[NSPredicate predicateWithFormat:@"%@!=%@", kModuleInfoNameKey, NSStringFromClass(moduleClass)]];

// 遍历所有模块
__block NSInteger index = -1;
[self.BHModules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:moduleClass]) {
// 获取moduleClass在数组里的下标
index = idx;
*stop = YES;
}
}];
if (index >= 0) {
// 移除moduleClass
[self.BHModules removeObjectAtIndex:index];
}

// 遍历模块事件
[self.BHModulesByEvent enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSMutableArray<id<BHModuleProtocol>> * _Nonnull obj, BOOL * _Nonnull stop) {
__block NSInteger index = -1;
// 遍历所有模块数组
[obj enumerateObjectsUsingBlock:^(id<BHModuleProtocol> _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:moduleClass]) {
index = idx;
*stop = NO;
}
}];
if (index >= 0) {
// 移除模块
[obj removeObjectAtIndex:index];
}
}];
}

/// 注册本地所有模块
- (void)registedAllModules
{
// self.BHModuleInfos已经在loadLocalModules方法中加载了,故使用plist静态注册,使用的顺序是:loadLocalModules --> registedAllModules
// 模块信息数组根据:级别、优先级进行倒序排列
[self.BHModuleInfos sortUsingComparator:^NSComparisonResult(NSDictionary *module1, NSDictionary *module2) {
NSNumber *module1Level = (NSNumber *)[module1 objectForKey:kModuleInfoLevelKey];
NSNumber *module2Level = (NSNumber *)[module2 objectForKey:kModuleInfoLevelKey];
if (module1Level.integerValue != module2Level.integerValue) {
return module1Level.integerValue > module2Level.integerValue;
} else {
NSNumber *module1Priority = (NSNumber *)[module1 objectForKey:kModuleInfoPriorityKey];
NSNumber *module2Priority = (NSNumber *)[module2 objectForKey:kModuleInfoPriorityKey];
return module1Priority.integerValue < module2Priority.integerValue;
}
}];

NSMutableArray *tmpArray = [NSMutableArray array];

//module init
// 遍历模块信息数组
[self.BHModuleInfos enumerateObjectsUsingBlock:^(NSDictionary *module, NSUInteger idx, BOOL * _Nonnull stop) {
// 获取类名
NSString *classStr = [module objectForKey:kModuleInfoNameKey];

Class moduleClass = NSClassFromString(classStr);
// 判断是否已经注册过了
BOOL hasInstantiated = ((NSNumber *)[module objectForKey:kModuleInfoHasInstantiatedKey]).boolValue;
if (NSStringFromClass(moduleClass) && !hasInstantiated) {
// 初始化
id<BHModuleProtocol> moduleInstance = [[moduleClass alloc] init];
// 添加到数组
[tmpArray addObject:moduleInstance];
}

}];

// 保存到模块数组里
[self.BHModules addObjectsFromArray:tmpArray];

// 注册所有系统事件
[self registerAllSystemEvents];
}


/**
注册自定义事件

@param eventType 事件类型eventType > 1000. 少于的话return,不做处理
@param moduleInstance 模块对象
@param selectorStr 方法字符串
*/
- (void)registerCustomEvent:(NSInteger)eventType
withModuleInstance:(id)moduleInstance
andSelectorStr:(NSString *)selectorStr {
if (eventType < 1000) {
return;
}
[self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr];
}
/**
触发事件

@param eventType 事件类型
*/
- (void)triggerEvent:(NSInteger)eventType
{
[self triggerEvent:eventType withCustomParam:nil];

}
/**
触发事件

@param eventType 事件类型
@param customParam 自定义参数
*/
- (void)triggerEvent:(NSInteger)eventType
withCustomParam:(NSDictionary *)customParam {
[self handleModuleEvent:eventType forTarget:nil withCustomParam:customParam];
}


#pragma mark - life loop

- (instancetype)init
{
self = [super init];
if (self) {
self.BHModuleDynamicClasses = [NSMutableArray array];
}
return self;
}


#pragma mark - private
/// 获取枚举类型
- (BHModuleLevel)checkModuleLevel:(NSUInteger)level
{
switch (level) {
case 0:
return BHModuleBasic;
break;
case 1:
return BHModuleNormal;
break;
default:
break;
}
//default normal
return BHModuleNormal;
}


/**
添加模块

@param object 模块对象
@param shouldTriggerInitEvent 是否要触发初始化事件
*/
- (void)addModuleFromObject:(id)object
shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
Class class;
NSString *moduleName = nil;

// 获取模块对象的类名
if (object) {
class = object;
moduleName = NSStringFromClass(class);
} else {
return ;
}

// 遍历BHModules数组,判断是否已经添加过了,有的话直接return掉。
__block BOOL flag = YES;
[self.BHModules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:class]) {
flag = NO;
*stop = YES;
}
}];
if (!flag) {
return;
}

// 判断模块类是否显示BHModuleProtocol协议。(到这里就可以回答文章上面为什么要实现这个协议?的疑问,你不实现协议没法进入if里面继续执行啊)
if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {
NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];

// 判断类是否包含basicModuleLevel方法
BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];

int levelInt = 1;
// 有包含,级别为0
if (responseBasicLevel) {
levelInt = 0;
}

// 把级别、类名保存到字典里
[moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
if (moduleName) {
[moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
}

// 添加到模块信息数组里
[self.BHModuleInfos addObject:moduleInfo];

//初始化模块对象
id<BHModuleProtocol> moduleInstance = [[class alloc] init];
// 添加到模块数组里
[self.BHModules addObject:moduleInstance];
// 设置为已注册
[moduleInfo setObject:@(YES) forKey:kModuleInfoHasInstantiatedKey];

// 根据级别对模块数组的模块对象进行排序
[self.BHModules sortUsingComparator:^NSComparisonResult(id<BHModuleProtocol> moduleInstance1, id<BHModuleProtocol> moduleInstance2) {
// 默认两个对象级别是1
NSNumber *module1Level = @(BHModuleNormal);
NSNumber *module2Level = @(BHModuleNormal);
// 判断对象是否实现basicModuleLevel,有的话级别是0
if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) {
module1Level = @(BHModuleBasic);
}
if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) {
module2Level = @(BHModuleBasic);
}
if (module1Level.integerValue != module2Level.integerValue) {
return module1Level.integerValue > module2Level.integerValue;
} else {
// 默认两个对象的优先级是0
NSInteger module1Priority = 0;
NSInteger module2Priority = 0;
// 判断对象是否实现modulePriority方法,获取优先级数,数越大优先级越高
if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) {
module1Priority = [moduleInstance1 modulePriority];
}
if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) {
module2Priority = [moduleInstance2 modulePriority];
}
return module1Priority < module2Priority;
}
}];
// 根据对象注册事件
[self registerEventsByModuleInstance:moduleInstance];

// 如果开启触发事件
if (shouldTriggerInitEvent) {
// 处理BHMSetupEvent事件
[self handleModuleEvent:BHMSetupEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
// 处理BHMInitEvent事件
[self handleModulesInitEventForTarget:moduleInstance withCustomParam:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// 处理BHMSplashEvent事件
[self handleModuleEvent:BHMSplashEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
});
}
}
}

/// 注册所有系统事件
- (void)registerAllSystemEvents
{
// 遍历所有模块
[self.BHModules enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
// 注册模块事件
[self registerEventsByModuleInstance:moduleInstance];
}];
}


/**
注册模块事件

@param moduleInstance 模块对象
*/
- (void)registerEventsByModuleInstance:(id<BHModuleProtocol>)moduleInstance
{
// 获取所有事件值(枚举值),所以添加自定义事件枚举变量时,记得在BHSelectorByEvent也要添加
NSArray<NSNumber *> *events = self.BHSelectorByEvent.allKeys;
// 遍历所有事件枚举值
[events enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 注册事件
[self registerEvent:obj.integerValue withModuleInstance:moduleInstance andSelectorStr:self.BHSelectorByEvent[obj]];
}];
}

/**
注册事件(此方法只是单纯对实例和方法进行注册,添加到对应的数组内,并未触发事件的条用)

@param eventType 事件类型
@param moduleInstance 模块对象
@param selectorStr 方法字符串
*/
- (void)registerEvent:(NSInteger)eventType
withModuleInstance:(id)moduleInstance
andSelectorStr:(NSString *)selectorStr {
// 字符串转SEL
SEL selector = NSSelectorFromString(selectorStr);
// 如果对象没有实现这个方法。return
if (!selector || ![moduleInstance respondsToSelector:selector]) {
return;
}
NSNumber *eventTypeNumber = @(eventType);
// 如果BHSelectorByEvent没有包含事件名称,则添加新的方法
if (!self.BHSelectorByEvent[eventTypeNumber]) {
[self.BHSelectorByEvent setObject:selectorStr forKey:eventTypeNumber];
}
// 如果BHModulesByEvent没有包含模块实例数组,则添加新的数组
if (!self.BHModulesByEvent[eventTypeNumber]) {
[self.BHModulesByEvent setObject:@[].mutableCopy forKey:eventTypeNumber];
}
// 根据事件类型获取模块实例数组
NSMutableArray *eventModules = [self.BHModulesByEvent objectForKey:eventTypeNumber];
// 判断实例是否在模块实例数组内
if (![eventModules containsObject:moduleInstance]) {
[eventModules addObject:moduleInstance];
// 将模块实例数组进行倒序排序
[eventModules sortUsingComparator:^NSComparisonResult(id<BHModuleProtocol> moduleInstance1, id<BHModuleProtocol> moduleInstance2) {
NSNumber *module1Level = @(BHModuleNormal);
NSNumber *module2Level = @(BHModuleNormal);
// 级别
if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) {
module1Level = @(BHModuleBasic);
}
if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) {
module2Level = @(BHModuleBasic);
}
if (module1Level.integerValue != module2Level.integerValue) {
return module1Level.integerValue > module2Level.integerValue;
} else {
// 优先级
NSInteger module1Priority = 0;
NSInteger module2Priority = 0;
if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) {
module1Priority = [moduleInstance1 modulePriority];
}
if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) {
module2Priority = [moduleInstance2 modulePriority];
}
return module1Priority < module2Priority;
}
}];
}
}

到这里总结一下。可能看到代码和注释知道每个方法是干什么的,但是看完可能有点乱有点绕,对使用者或许你不需要知道底层是如何实现的,只要用就行了,但是怎么用呢?BHModuleManager的接口已经非常清楚了,你使用的步骤总体来说就是两步:注册、调用。这两个步骤几乎贯穿整个框架,无论你触发什么事件、需要什么内容,一定要先注册。而注册只是单纯的初始化和添加到内存,并不会主动触发事件,所以要触发事件还需要你主动。

接下来看看处理事件的几个私有方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#pragma mark - module protocol

/**
处理模块事件

@param eventType 事件类型
@param target 实例
@param customParam 参数
*/
- (void)handleModuleEvent:(NSInteger)eventType
forTarget:(id<BHModuleProtocol>)target
withCustomParam:(NSDictionary *)customParam
{
switch (eventType) {
case BHMInitEvent:
//special特别的,处理模块初始化事件
[self handleModulesInitEventForTarget:nil withCustomParam :customParam];
break;
case BHMTearDownEvent:
//special特别的,处理模块拆除事件
[self handleModulesTearDownEventForTarget:nil withCustomParam:customParam];
break;
default: {
// 获取方法名称,其他统统走这个方法
NSString *selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
[self handleModuleEvent:eventType forTarget:nil withSeletorStr:selectorStr andCustomParam:customParam];
}
break;
}

}


/**
处理初始化方法(之前提到这个方法是特别,为啥特别呢,因为是这个方法可以是异步的)

@param target 对象
@param customParam 参数
*/
- (void)handleModulesInitEventForTarget:(id<BHModuleProtocol>)target
withCustomParam:(NSDictionary *)customParam
{
// 获取上下文
BHContext *context = [BHContext shareInstance].copy;
context.customParam = customParam;
context.customEvent = BHMInitEvent;

// 获取事件对应的模块实例数组
NSArray<id<BHModuleProtocol>> *moduleInstances;
if (target) {
moduleInstances = @[target];
} else {
moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMInitEvent)];
}

// 遍历所有对象
[moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
__weak typeof(&*self) wself = self;
// 初始化对象调取modInit:方法的block
void ( ^ bk )();
bk = ^(){
__strong typeof(&*self) sself = wself;
if (sself) {
if ([moduleInstance respondsToSelector:@selector(modInit:)]) {
[moduleInstance modInit:context];
}
}
};

// 记录事件时间
[[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- modInit:", [moduleInstance class]]];

// 判断对象是否实现异步方法
if ([moduleInstance respondsToSelector:@selector(async)]) {
BOOL async = [moduleInstance async];

// 如果是异步,异步调用block
if (async) {
dispatch_async(dispatch_get_main_queue(), ^{
bk();
});
// 直接调用
} else {
bk();
}
} else {
bk();
}
}];
}


/**
处理模块卸载事件(特别之处是倒序卸载的事件的)

@param target 对象
@param customParam 参数
*/
- (void)handleModulesTearDownEventForTarget:(id<BHModuleProtocol>)target
withCustomParam:(NSDictionary *)customParam
{
// 获取上下文
BHContext *context = [BHContext shareInstance].copy;
context.customParam = customParam;
context.customEvent = BHMTearDownEvent;

// 获取事件对应的模块实例数组
NSArray<id<BHModuleProtocol>> *moduleInstances;
if (target) {
moduleInstances = @[target];
} else {
moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMTearDownEvent)];
}

//Reverse Order to unload 倒序开始卸载模块事件
for (int i = (int)moduleInstances.count - 1; i >= 0; i--) {
id<BHModuleProtocol> moduleInstance = [moduleInstances objectAtIndex:i];
if (moduleInstance && [moduleInstance respondsToSelector:@selector(modTearDown:)]) {
[moduleInstance modTearDown:context];
}
}
}


/**
处理模块事件(统一处理)

@param eventType 事件类型
@param target 对象
@param selectorStr 方法名称
@param customParam 参数
*/
- (void)handleModuleEvent:(NSInteger)eventType
forTarget:(id<BHModuleProtocol>)target
withSeletorStr:(NSString *)selectorStr
andCustomParam:(NSDictionary *)customParam
{
// 获取程序上下文
BHContext *context = [BHContext shareInstance].copy;
// 设置事件参数
context.customParam = customParam;
// 设置事件类型
context.customEvent = eventType;

// 获取方法seletor
if (!selectorStr.length) {
selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
}
SEL seletor = NSSelectorFromString(selectorStr);
if (!seletor) {
selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
seletor = NSSelectorFromString(selectorStr);
}
// 获取模块对象数组
NSArray<id<BHModuleProtocol>> *moduleInstances;
if (target) {
moduleInstances = @[target];
} else {
moduleInstances = [self.BHModulesByEvent objectForKey:@(eventType)];
}
// 遍历对象数组
[moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
// 使用runtime执行方法
if ([moduleInstance respondsToSelector:seletor]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[moduleInstance performSelector:seletor withObject:context];
#pragma clang diagnostic pop
// 记录对象和方法调用
[[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- %@", [moduleInstance class], NSStringFromSelector(seletor)]];

}
}];
}

除了两个特别的方法(BHMInitEvent、BHMTearDownEvent),其他都是runtime方法调取的。这里要说明一下BHMTearDownEvent是没有在BHAppDelegate调用的,那么就是说这个方法暂时不会系统调用。这个事件做什么用处的呢?有时候我们注册了某个模块事件,但是模块事件使用完毕,想释放掉模块的事件,就需要用到这个方法了。只要在模块类中实现modTearDown:方法,可以操作相关的卸载工作。另外目前框架还没有释放真个模块的API,期待作者后期补上。

BHServiceManager

其实BHServiceManager跟BHModuleManager类似,只是注册的东西不一样而已,BHModuleManager对整个模块的类进行BHServiceManager注册。而已BHServiceManager只对某个类进行注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// 是否需要抛出异常
@property (nonatomic, assign) BOOL enableException;

+ (instancetype)sharedManager;

/// 注册本地plist服务
- (void)registerLocalServices;

/**
注册服务

@param service 服务
@param implClass 类
*/
- (void)registerService:(Protocol *)service implClass:(Class)implClass;

/**
获取服务

@param service 服务
@return 对象实例
*/
- (id)createService:(Protocol *)service;

BHContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
typedef enum
{
BHEnvironmentDev = 0,
BHEnvironmentTest,
BHEnvironmentStage,
BHEnvironmentProd
}BHEnvironmentType;


@interface BHContext : NSObject <NSCopying>

//global env 程序环境类型
@property(nonatomic, assign) BHEnvironmentType env;

//global config 全局配置信息类
@property(nonatomic, strong) BHConfig *config;

//application appkey
@property(nonatomic, strong) NSString *appkey;
//customEvent>=1000
@property(nonatomic, assign) NSInteger customEvent;

@property(nonatomic, strong) UIApplication *application;

@property(nonatomic, strong) NSDictionary *launchOptions;
/// 模块配置文件路径
@property(nonatomic, strong) NSString *moduleConfigName;
/// 服务配置文件路径
@property(nonatomic, strong) NSString *serviceConfigName;

//3D-Touch model
#if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400
@property (nonatomic, strong) BHShortcutItem *touchShortcutItem;
#endif

//OpenURL model
@property (nonatomic, strong) BHOpenURLItem *openURLItem;

//Notifications Remote or Local通知相关
@property (nonatomic, strong) BHNotificationsItem *notificationsItem;

//user Activity Model用户活动
@property (nonatomic, strong) BHUserActivityItem *userActivityItem;

//watch Model手表相关参数模型
@property (nonatomic, strong) BHWatchItem *watchItem;

//custom param自定义参数
@property (nonatomic, copy) NSDictionary *customParam;

+ (instancetype)shareInstance;
/// 保存对象&服务协议名称(对象必须是实现服务协议)
- (void)addServiceWithImplInstance:(id)implInstance serviceName:(NSString *)serviceName;
/// 根据服务协议名称移除对象(对象必须是实现服务协议)
- (void)removeServiceWithServiceName:(NSString *)serviceName;
/// 根据服务协议名称获取对象(对象必须是实现服务协议)
- (id)getServiceInstanceFromServiceName:(NSString *)serviceName;

@end

BHContext作为一个全局对象,所有系统事件处理,都可以在这里获取。写到这里BeeHive基本看完。当然其他功能我就不做深究,我想通过这次粗略的阅读,为以后的实践做准备。可能下次就是一个小demo的实践了。