BeeHive

前言

BeeHive 是阿里用于iOS的 App 模块化编程的框架实现方案,吸收了 Spring 框架 Service 的理念来实现模块间的 API 耦合。

项目结构

项目模块

├── BHAnnotation.h
├── BHAnnotation.m
├── BHAppDelegate.h
├── BHAppDelegate.m
├── BHCommon.h
├── BHConfig.h
├── BHConfig.m
├── BHContext.h
├── BHContext.m
├── BHDefines.h
├── BHModuleManager.h
├── BHModuleManager.m
├── BHModuleProtocol.h
├── BHServiceManager.h
├── BHServiceManager.m
├── BHServiceProtocol.h
├── BHTimeProfiler.h
├── BHTimeProfiler.m
├── BHWatchDog.h
├── BHWatchDog.m
├── BeeHive.h
└── BeeHive.m

模块解读

BeeHive:是框架的门面,将几个组件封装在这个类里面组合,向外提供统一的接口。
BHAnnotation:注解类,提供一些简便的宏
BHAppDelegate:AppDelegate 的替换类,提供一些启动,进入后台的消息分发到模块的功能。
BHCommon:Log 功能(暂时,也许会增加)
BHConfig:提供全局的配置功能,类似于 NSUserDefaults 的功能
BHContext:框架的上下文对象,可以配置环境(dev, stage等),提供全局配置对象 BHConfig,还有一些关于 APP 上下文的功能。
BHModuleManager:模块的管理类,包括读取,加载,注册模块等功能。
BHModuleProtocol:要成为一个模块需要遵守的协议。
BHServiceManager:服务,也就是模块间的通信用服务来解决。
BHServiceProtocol:要成为服务需要遵守的协议。
BHTimeProfiler:提供性能监控的功能
BHWatchDog:检测线程的卡顿功能。

如何解耦

在 BeeHive 中一个个对象被分成模块(Module),模块之间相互独立,通过接口来调用(Service)来让模块间不相互之间依赖,通过暴露的接口来规范调用机制,但是过多的接口也会造成维护问题。

模块

想让自己的对象变成 Module 只需要遵守协议 BHModuleProtocolBHModuleProtocol 协议中包含了一个模块的声明周期的回调,和注册的方法,如提供 AppDelegate 周期回调的事件,以及模块初始化,配置入口等

/// 一部分事件
- (void)basicModuleLevel;

- (BOOL)async;

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

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

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

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

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

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

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

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

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

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

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

此外,模块注册方法提供从 plist 注册,使用宏BH_EXPORT_MODULE注册 2 种注册方法。

模块的注册

模块的注册主要集中在 BHModuleManager 这个对象单例中。
读取 plist 的模块

- (void)loadLocalModules
{
    
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
        return;
    }
    
    NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
    
    NSArray *modulesArray = [moduleList objectForKey:kModuleArrayKey];
    
    [self.BHModules addObjectsFromArray:modulesArray];
    
}

动态注册的方式,也就是宏注册

- (void)addModuleFromObject:(id)object
{
    Class class;
    NSString *moduleName = nil;
    
    if (object) {
        class = object;
        moduleName = NSStringFromClass(class);
    } else {
        return ;
    }
    
    if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {
        NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];
        
        BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];

        int levelInt = 1;
        
        if (responseBasicLevel) {
            levelInt = 0;
        }
        
        [moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
        if (moduleName) {
            [moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
        }

        [self.BHModules addObject:moduleInfo];
    }
}

这个类除注册之外,还提供了事件的转发,这部分代码比较简单,想了解可以查看源代码。

接口 (Service)

接口规范了统一的调用方式,避免了直接依赖来调用对象方法,但是这样的做法如果对象多了,接口也会变得很多,维护接口也是一个巨大的工作量。
定义一个接口需要遵守BHServiceProtocol 协议,此协议主要是为了模块的单例实现提供服务。

接口注册部分也分为 2 种方式,一种是 plist,一种是代码注册。
用 plist 注册:

- (void)registerLocalServices
{
    NSString *serviceConfigName = [BHContext shareInstance].serviceConfigName;
    
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist"];
    if (!plistPath) {
        return;
    }
    
    NSArray *serviceList = [[NSArray alloc] initWithContentsOfFile:plistPath];
    
    [self.lock lock];
    for (NSDictionary *dict in serviceList) {
        NSString *protocolKey = [dict objectForKey:@"service"];
        NSString *protocolImplClass = [dict objectForKey:@"impl"];
        if (protocolKey.length > 0 && protocolImplClass.length > 0) {
            [self.allServicesDict addEntriesFromDictionary:@{protocolKey:protocolImplClass}];
        }
    }
    [self.lock unlock];
}

使用代码也就是类似

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

实现如下

- (void)registerService:(Protocol *)service implClass:(Class)implClass
{
    NSParameterAssert(service != nil);
    NSParameterAssert(implClass != nil);
    
    if (![implClass conformsToProtocol:service] && self.enableException) {
        @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ module does not comply with %@ protocol", NSStringFromClass(implClass), NSStringFromProtocol(service)] userInfo:nil];
    }
    
    if ([self checkValidService:service] && self.enableException) {
        @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil];
    }
    
    NSString *key = NSStringFromProtocol(service);
    NSString *value = NSStringFromClass(implClass);
    
    if (key.length > 0 && value.length > 0) {
        [self.lock lock];
        [self.allServicesDict addEntriesFromDictionary:@{key:value}];
        [self.lock unlock];
    }
}

模块之间的调用

既然模块有了,接口也有了,那么他们是怎么调用的呢?
首先为了解耦,BeeHive 使用接口来解耦,那么调用方式会如下:

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

此方法,可以指定一个接口来完成对接口后面的模块的调用。

那么模块是否可以成为单例,对外界调用呢?
当然是可以的,只需要在Service对象中实现事件函数

声明

-(BOOL) singleton
{
    return YES;
}

通过createService获取的对象则为单例对象,如果实现上面函数返回的是NO,则createService返回的是多例。

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

好了,至此,BeeHive 的解耦的手法就是如此。

其他

BeeHive 还提供了环境变量的封装,BHContext 对象就是一个 APP 上下文的环境变量。想知道更详细的细节,您可以自己探索。

你可能感兴趣的:(BeeHive)