阿里组件化框架BeeHive解析

更好的阅读体验,请到个人博客阅读: 阿里组件化框架BeeHive解析

本文是基于BeeHive版本1.6.0进行分析。

BeeHive核心思想涉及两个部分:

  1. 各个模块间从直接调用对应模块,变成以Service的形式,避免了直接依赖。
  2. App生命周期的分发,将耦合在AppDelegate中的逻辑拆分,每个模块以微应用的形式独立存在。

Core+Plugin的形式可以让一个应用主流程部分得到集中管理,不同模块以plugin形式存在,便于横向的扩展和移植。

本文会按照以下顺序进行介绍:

  • [BeeHive概览]
  • [BeeHive模块生命周期事件]
    • [系统事件]
    • [通用事件]
    • [业务自定义事件]
  • [BeeHive模块注册]
    • [Annotation方式注册]
    • [读取本地Plist方式注册]
    • [Load方法注册]
  • [BeeHive模块间调用]
    • [Annotation方式注册]
    • [读取本地Plist方式注册]
    • [API注册]
  • [上下文环境Context]

BeeHive概览

BeeHive的架构图如下所示:

《阿里组件化框架BeeHive解析》 BeeHive架构图

图中的BHContext,包含BeeHive的配置文件,提供全局统一上下文信息。

图中的BHCore包含:

  • BeeHive,提供组件库对外接口
  • BHModuleManager和BHModuleProtocol,注册和创建Module逻辑
  • BHServiceManager和BHServiceProtocol,注册和创建Service逻辑
  • BHRouter

Module、Service注册和调用逻辑只和核心模块相关,Module之间没有直接的关联关系。

对于Module和Service的注册,BeeHive提供了三种不同的形式:静态plist,动态注册,annotation。Module、Service之间没有关联,每个业务模块可以单独实现Module或者Service的功能。

《阿里组件化框架BeeHive解析》 Module注册

图中包含了主要的BeeHive启动过程以及Module注册的时序逻辑。Module的事件分发源于BHAppDelegate中的triggerEvent

加载Module:

  1. BeeHive.plist中配置的module和service是在 AppDelegate中调用 [[BeeHive shareInstance] setContext:[BHContext shareInstance]]; 时加载。
  2. Module的实现中 +load内部调用 [BeeHive registerDynamicModule:[self class]]; 动态加载。
  3. Module的实现中使用注解: @BeeHiveMod(XXModule)

BHAppDelegate中除了回调系统的事件,还将App生命周期进行扩展,增加ModuleSetupModuleInitModuleSplash,此外开发人员还可以自行扩展。

《阿里组件化框架BeeHive解析》 BeeHive事件扩展

扩展周期过程中,同时加入Module分析量化功能,每个模块Init的耗时均可计算出来,为性能优化做到数据上的支持。一个App的业务增多过程中,通过分析定位Module的Init耗时可以确定需要优化的Module。

Module遵循BHModuleProtocol后,能够捕获App状态的回调,并拥有App生命周期内的全局上下文,通过context可获取配置参数,模块资源以及服务资源。

以BeeHive作为底层框架的App,除了解耦带来的便利,开发人员在开发新App过程中涉及相同功能的Module,无需重复造轮子,直接移植Module,开发一个App如同拼装积木,能组合需要的功能业务。

BHModuleManager.m:

//BHModuleInfos: [{moduleClass:String, ModuleLevel:NSNumber, modulePriority:String}]
//BHModules: [id<BHModuleProtocol>]

@interface BHModuleManager()

@property(nonatomic, strong) NSMutableArray *BHModuleDynamicClasses;

@property(nonatomic, strong) NSMutableArray<NSDictionary *> *BHModuleInfos;
@property(nonatomic, strong) NSMutableArray<id<BHModuleProtocol>> *BHModules;

@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSMutableArray<id<BHModuleProtocol>> *> *BHModulesByEvent;
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSString *> *BHSelectorByEvent;

@end

BHModuleInfosBHModulesByEventid<BHModuleProtocol>均按照moduleInstancesmoduleLevelmodulePriority进行了排序。

《阿里组件化框架BeeHive解析》 注册静态plist service

上图中包含注册静态plist中service的相关逻辑,App启动时通过BeeHive的setContext:来触发plist中service的注册。service的注册并没有创建对应的service实例,只是在BHServiceManager中建立Service协议实现该协议的类之间的映射关系。

《阿里组件化框架BeeHive解析》 动态注册service

上图中是动态注册service的逻辑,这是在App启动事件分发时触发。Module根据需求动态注册某个服务,通常,注册service的代码在module的modInit:或者modSetup:中。

《阿里组件化框架BeeHive解析》 创建service实例

业务组件可以通过createService:直接调用服务。Service的调用和实现,核心是BHServiceManager。可以单独创建Services Interface Pod,统一放置要用的Services,这样的业务依赖就从网状式变成中心式,业务方只需依赖Services一个。

Service可以按需加载,BeeHive逻辑是将基础服务注册在plist中,业务型服务允许Service不先注册,直到业务需要时才被动态注册。

Service支持两种不同模式:

  • 单例: 对于全局统一且无状态服务,建议使用这种创建形式,这样有利于Service的统一管理以及减少不必要内存消耗。
  • 多实例: 每次调用服务都重新创建新的服务,对于涉及状态以及状态变化的服务最适合使用多实例方式。

在多线程环境下遇到了Service读写问题,已通过Lock来已避免Array crash问题。
不过Service还存在如下问题:

  • Service依赖关系,导致底层依赖的Service没有被创建时就被调用。
  • 规划Service、Module创建顺序,使得App达到秒开,优化性能体验。

前者依赖问题计划通过调度机制来解决,后者还需要将AppDelegate更多业务剥离以及实践才可。

BeeHive使用createService:createService:withServiceName:来创建实现了协议的对象,并且缓存该对象。

BeeHive模块生命周期事件

BeeHive会给每个模块提供生命周期事件,用于与BeeHive宿主环境进行必要信息交互,感知模块生命周期的变化。

事件分为三种类型:

  • 系统事件
  • 通用事件
  • 业务自定义事件

在BHModuleManager的头文件中,Event的类型定义如下:

typedef NS_ENUM(NSInteger, BHModuleEventType)
{
    BHMSetupEvent = 0,
    BHMInitEvent,
    BHMTearDownEvent,
    BHMSplashEvent,
    BHMQuickActionEvent,
    BHMWillResignActiveEvent,
    BHMDidEnterBackgroundEvent,
    BHMWillEnterForegroundEvent,
    BHMDidBecomeActiveEvent,
    BHMWillTerminateEvent,
    BHMUnmountEvent,
    BHMOpenURLEvent,
    BHMDidReceiveMemoryWarningEvent,
    BHMDidFailToRegisterForRemoteNotificationsEvent,
    BHMDidRegisterForRemoteNotificationsEvent,
    BHMDidReceiveRemoteNotificationEvent,
    BHMDidReceiveLocalNotificationEvent,
    BHMWillPresentNotificationEvent,
    BHMDidReceiveNotificationResponseEvent,
    BHMWillContinueUserActivityEvent,
    BHMContinueUserActivityEvent,
    BHMDidFailToContinueUserActivityEvent,
    BHMDidUpdateUserActivityEvent,
    BHMHandleWatchKitExtensionRequestEvent,
    BHMDidCustomEvent = 1000
    
};

系统事件

《阿里组件化框架BeeHive解析》 BeeHive系统事件

系统事件通常是Application生命周期事件,例如WillResignActiveEvent, DidEnterBackgroundEvent, WillEnterForegroundEvent, DidBecomeActiveEvent, WillTerminateEvent

一般做法是使用BHAppDelegate来接管系统事件,如下所示:

//TestAppDelegate.h
#import "BeeHive.h"

@interface TestAppDelegate : BHAppDelegate <UIApplicationDelegate>

//TestAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [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"];

    
    [super application:application didFinishLaunchingWithOptions:launchOptions];
    
...
    return YES;
}

在BHAppDelegate的实现文件中,实现了一系列的系统调用事件。如下所示:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[BHModuleManager sharedManager] triggerEvent:BHMSetupEvent];
    [[BHModuleManager sharedManager] triggerEvent:BHMInitEvent];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [[BHModuleManager sharedManager] triggerEvent:BHMSplashEvent];
    });
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
    if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0f) {
        [UNUserNotificationCenter currentNotificationCenter].delegate = self;
    }
#endif
    
#ifdef DEBUG
    [[BHTimeProfiler sharedTimeProfiler] saveTimeProfileDataIntoFile:@"BeeHiveTimeProfiler"];
#endif
    
    return YES;
}


#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80400 

-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
    [[BeeHive shareInstance].context.touchShortcutItem setShortcutItem: shortcutItem];
    [[BeeHive shareInstance].context.touchShortcutItem setScompletionHandler: completionHandler];
    [[BHModuleManager sharedManager] triggerEvent:BHMQuickActionEvent];
}
#endif

- (void)applicationWillResignActive:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMWillResignActiveEvent];
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMDidEnterBackgroundEvent];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMWillEnterForegroundEvent];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMDidBecomeActiveEvent];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMWillTerminateEvent];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    [[BeeHive shareInstance].context.openURLItem setOpenURL:url];
    [[BeeHive shareInstance].context.openURLItem setSourceApplication:sourceApplication];
    [[BeeHive shareInstance].context.openURLItem setAnnotation:annotation];
    [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent];
    return YES;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80400
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
  
    [[BeeHive shareInstance].context.openURLItem setOpenURL:url];
    [[BeeHive shareInstance].context.openURLItem setOptions:options];
    [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent];
    return YES;
}
#endif


- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveMemoryWarningEvent];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    [[BeeHive shareInstance].context.notificationsItem setNotificationsError:error];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToRegisterForRemoteNotificationsEvent];
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    [[BeeHive shareInstance].context.notificationsItem setDeviceToken: deviceToken];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidRegisterForRemoteNotificationsEvent];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo];
    [[BeeHive shareInstance].context.notificationsItem setNotificationResultHander: completionHandler];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent];
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    [[BeeHive shareInstance].context.notificationsItem setLocalNotification: notification];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveLocalNotificationEvent];
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity
{
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity];
        [[BHModuleManager sharedManager] triggerEvent:BHMDidUpdateUserActivityEvent];
    }
}

- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error
{
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType];
        [[BeeHive shareInstance].context.userActivityItem setUserActivityError: error];
        [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToContinueUserActivityEvent];
    }
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity];
        [[BeeHive shareInstance].context.userActivityItem setRestorationHandler: restorationHandler];
        [[BHModuleManager sharedManager] triggerEvent:BHMContinueUserActivityEvent];
    }
    return YES;
}

- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType
{
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType];
        [[BHModuleManager sharedManager] triggerEvent:BHMWillContinueUserActivityEvent];
    }
    return YES;
}
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(nullable NSDictionary *)userInfo reply:(void(^)(NSDictionary * __nullable replyInfo))reply {
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        [BeeHive shareInstance].context.watchItem.userInfo = userInfo;
        [BeeHive shareInstance].context.watchItem.replyHandler = reply;
        [[BHModuleManager sharedManager] triggerEvent:BHMHandleWatchKitExtensionRequestEvent];
    }
}
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
    [[BeeHive shareInstance].context.notificationsItem setNotification: notification];
    [[BeeHive shareInstance].context.notificationsItem setNotificationPresentationOptionsHandler: completionHandler];
    [[BeeHive shareInstance].context.notificationsItem setCenter:center];
    [[BHModuleManager sharedManager] triggerEvent:BHMWillPresentNotificationEvent];
};

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
    [[BeeHive shareInstance].context.notificationsItem setNotificationResponse: response];
    [[BeeHive shareInstance].context.notificationsItem setNotificationCompletionHandler:completionHandler];
    [[BeeHive shareInstance].context.notificationsItem setCenter:center];
    [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveNotificationResponseEvent];
};
#endif

@end

这样所有的系统事件都可以通过BHModuleManager的triggerEvent:来处理。

在上述事件中,BHMInitEventBHMTearDownEvent事件需要做特殊处理。

先看看BHMInitEvent的处理。

- (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;
        void ( ^ bk )(void);
        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];
            
            if (async) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    bk();
                });
                
            } else {
                bk();
            }
        } else {
            bk();
        }
    }];
}

BHMInitEvent事件是触发各个module启动时的初始化逻辑。这里从self.BHModulesByEvent中取出BHMInitEvent事件对应的module数组,遍历其中的每个module实例,向其发送modInit:消息。这里会考虑是否异步执行module的初始化。如果moduleInstance重写了async方法,那么就会根据该方法的返回值来决定是否异步执行module的初始化。

modInit:方法由各个module实例各自实现,可以在其中注册一个外部模块可以访问的Service接口。

-(void)modInit:(BHContext *)context
{
    //注册模块的接口服务
    [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
}

再看看BHMTearDownEvent事件。这个事件中可以处理module的清理工作。

- (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];
        }
    }
}

由于module具有优先级,且self.BHModulesByEvent结构中,每种事件类型对应的modules数组中的module元素都已经按照优先级从高到低排列,因此逆序对modules数组中的module元素调用modTearDown:方法。

通用事件

在系统事件的基础之上,扩展了应用的通用事件,例如modSetupmodInit等,可以用于编码实现各插件模块的设置与初始化。

扩展的通用事件如下:

《阿里组件化框架BeeHive解析》 BeeHive通用事件

所有的事件都可以通过调用BHModuleManager的triggerEvent:来处理。

- (void)triggerEvent:(NSInteger)eventType
{
    [self triggerEvent:eventType withCustomParam:nil];
    
}

- (void)triggerEvent:(NSInteger)eventType
     withCustomParam:(NSDictionary *)customParam {
    [self handleModuleEvent:eventType forTarget:nil withCustomParam: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;
    }
}

- (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;
    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) {
        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)]];
            
        }
    }];
}

从上面的代码可以看出,事件类型分发是在方法handleModuleEvent:forTarget:withCustomParam:中进行。如之前所述,需要对BHMInitEventBHMTearDownEvent做特殊处理。同时,触发各个module(从self.BHModulesByEvent中获取)中的响应事件方法通过performSelector:withObject:来调用。

注意这里的module都是遵循BHModuleProtocol协议的。

通用事件中,可以在modSetup中设置环境变量,通过context.env可以判断我们的应用环境状态来决定我们如何配置我们的应用。如下所示:

-(void)modSetup:(BHContext *)context
{
    switch (context.env) {
        case BHEnvironmentDev:
        //....初始化开发环境
        break;
        case BHEnvironmentProd:
        //....初始化生产环境
        default:
        break;
    }
}

业务自定义事件

如果觉得系统事件、通用事件不足以满足需要,我们还将事件封装简化成BHAppdelgate,你可以通过继承 BHAppdelegate来扩展自己的事件。通过以下方式来注册自定义事件:

- (void)registerCustomEvent:(NSInteger)eventType
   withModuleInstance:(id)moduleInstance
       andSelectorStr:(NSString *)selectorStr {
    if (eventType < 1000) {
        return;
    }
    [self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr];
}

触发带参数的事件响应:

- (void)triggerEvent:(NSInteger)eventType
     withCustomParam:(NSDictionary *)customParam {
    [self handleModuleEvent:eventType forTarget:nil withCustomParam:customParam];
}

BeeHive模块注册

使用注解的方式注册Module和Service时,Module和Service的注册发生在加载镜像文件时期。

plist方式注册Module和Service,是在AppDelegate中设置BeeHive的Context时加载注册。

Module动态注册是在+load方法中,也是在加载镜像时注册。Service的动态注册可以Module的modInit:或者modSetup:中,或者使用时注册。

模块注册有三种方式:Annotation方式注册、读取本地plist方式注册、Load方法注册。所谓注册,就是将Module类告知BHModuleManager来管理。由此可见,在BeeHive中是通过BHModuleManager来管理各个模块的,BHModuleManager中只会管理已经被注册过的模块。

Annotation方式注册

通过BeeHiveMod宏进行Annotation标记。

@BeeHiveMod(ShopModule)

BeeHiveMod宏定义如下:

#define BeeHiveMod(name) \
class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name"";

BeeHiveDATA也是一个宏:

#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname"")))

在预编译结束后,BeeHiveMode宏最终会完全展开成下面的样子:

@class BeeHive; char * kShopModule_mod __attribute((used, section("__DATA,""BeehiveMods"""))) = """ShopModule""";

这里需要注意双引号的总对数。

关于__attribute的用法,可参考我的另一篇attribute

__attribute第一个参数used,它的作用是告诉编译器,我声明的这个符号是需要保留的。被used修饰以后,意味着即使函数没有被引用,在Release下也不会被优化。如果不加这个修饰,那么Release环境链接器会去掉没有被引用的段。

有时候我们需要指定一个特殊的段,来存放我们想要的数据。这里我们就把数据存在__DATA数据段里面的”BeehiveMods”section中。 Attributes的修饰关键字section ("section-name”)可以达到此要求。

上述代码中:

@class BeeHive; char * kShopModule_mod __attribute((used, section("__DATA,""BeehiveMods"""))) = """ShopModule""";

去掉__attribute的属性,相当于:

@class BeeHive; char * kShopModule_mod = """ShopModule""";

只不过是将kShopModule_mod变量存储在了__DATA段的BeehiveMods section中。

这样,所有的Module类名的字符串都会放置在__DATA段BeehiveMods section中,那么如何取出这些字符串呢?

这里先介绍一下__attribute__((constructor))

constructor:顾名思义,构造器加上这个属性的函数会在可执行文件(或 shared library)load时被调用,可以理解为在 main() 函数调用前执行:

__attribute__((constructor))
static void beforeMain(void) {
    NSLog(@"beforeMain");
}
__attribute__((destructor))
static void afterMain(void) {
    NSLog(@"afterMain");
}
int main(int argc, const char * argv[]) {
    NSLog(@"main");
    return 0;
}

// Console:
// "beforeMain" -> "main" -> “afterMain"

_dyld_register_func_for_add_image:这个函数是用来注册回调,当dyld链接符号时,调用此回调函数。在dyld加载镜像时,会执行注册过的回调函数;当然,我们也可以使用下面的方法注册自定义的回调函数,同时也会为所有已经加载的镜像执行回调:

/*
 * The following functions allow you to install callbacks which will be called   
 * by dyld whenever an image is loaded or unloaded.  During a call to _dyld_register_func_for_add_image()
 * the callback func is called for every existing image.  Later, it is called as each new image
 * is loaded and bound (but initializers not yet run).  The callback registered with
 * _dyld_register_func_for_remove_image() is called after any terminators in an image are run
 * and before the image is un-memory-mapped.
 */
extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) 
extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide))

对于每一个已经存在的镜像,当它被动态链接时,都会执行回调void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide),传入文件的mach_header以及一个虚拟内存地址 intptr_t。

__attribute__((constructor))
void initProphet() {
    _dyld_register_func_for_add_image(dyld_callback);
}

static void dyld_callback(const struct mach_header *mhp, intptr_t vmaddr_slide)
{
    NSArray *mods = BHReadConfiguration(BeehiveModSectName, mhp);
    for (NSString *modName in mods) {
        Class cls;
        if (modName) {
            cls = NSClassFromString(modName);
            
            if (cls) {
                [[BHModuleManager sharedManager] registerDynamicModule:cls];
            }
        }
    }
}


NSArray<NSString *>* BHReadConfiguration(char *sectionName,const struct mach_header *mhp)
{
    NSMutableArray *configs = [NSMutableArray array];
    unsigned long size = 0;
#ifndef __LP64__
    uintptr_t *memory = (uintptr_t*)getsectiondata(mhp, SEG_DATA, sectionName, &size);
#else
    const struct mach_header_64 *mhp64 = (const struct mach_header_64 *)mhp;
    uintptr_t *memory = (uintptr_t*)getsectiondata(mhp64, SEG_DATA, sectionName, &size);
#endif
    
    unsigned long counter = size/sizeof(void*);
    for(int idx = 0; idx < counter; ++idx){
        char *string = (char*)memory[idx];
        NSString *str = [NSString stringWithUTF8String:string];
        if(!str)continue;
        
        BHLog(@"config = %@", str);
        if(str) [configs addObject:str];
    }
    
    return configs;
}

mach_header是定义在usr/include/mach-o/loader.h中的数据结构:

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
     uint32_t    magic;        /* mach magic number identifier */
     cpu_type_t    cputype;    /* cpu specifier */
     cpu_subtype_t    cpusubtype;    /* machine specifier */
     uint32_t    filetype;    /* type of file */
     uint32_t    ncmds;        /* number of load commands */
     uint32_t    sizeofcmds;    /* the size of all the load commands */
     uint32_t    flags;        /* flags */
     uint32_t    reserved;    /* reserved */
};

通过调用BHReadConfiguration函数,我们就可以拿到之前注册到BeehiveMods特殊段里面的各个Module的类名,该函数返回类名字符串的数组。

然后将Module交由到BHModuleManager管理:

- (void)registerDynamicModule:(Class)moduleClass
{
    [self registerDynamicModule:moduleClass shouldTriggerInitEvent:NO];
}
- (void)registerDynamicModule:(Class)moduleClass
       shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
    [self addModuleFromObject:moduleClass shouldTriggerInitEvent:shouldTriggerInitEvent];
}

- (void)addModuleFromObject:(id)object
     shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
    Class class;
    NSString *moduleName = nil;
    
    if (object) {
        class = object;
        moduleName = NSStringFromClass(class);
    } else {
        return ;
    }
    
    /** 检测是否已存在Module类 */
    __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;
    }
    
    if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {
        NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];
        
        /** basicModuleLevel 这个方法如果不实现,Level是Normal: 1 */
        BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];
        int levelInt = 1;
        
        if (responseBasicLevel) {
            /** basicModuleLevel 这个方法如果实现,Level是Basic: 0 */
            levelInt = 0;
        }
        
        [moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
        if (moduleName) {
            [moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
        }
        [self.BHModuleInfos addObject:moduleInfo];
        
        /** 初始化module实例 */
        id<BHModuleProtocol> moduleInstance = [[class alloc] init];
        [self.BHModules addObject:moduleInstance];
        [moduleInfo setObject:@(YES) forKey:kModuleInfoHasInstantiatedKey];

        /** 将module按照优先级排序 */
        [self.BHModules 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;
            }
        }];

        /** 给module注册事件 */
        [self registerEventsByModuleInstance:moduleInstance];
        
        if (shouldTriggerInitEvent) {
            [self handleModuleEvent:BHMSetupEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
            [self handleModulesInitEventForTarget:moduleInstance withCustomParam:nil];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self handleModuleEvent:BHMSplashEvent forTarget:moduleInstance withSeletorStr:nil andCustomParam:nil];
            });
        }
    }
}

所有需要注册的Module必须遵循BHModuleProtocol协议,否则不会被存储。

读取本地Plist方式注册

首先需要设置本地Plist文件的读取路径:

[BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默认为BeeHive.bundle/BeeHive.plist

BeeHive的所有配置都可以通过BHContext进行传递。

Plist文件的字段如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>URLGlobalScheme</key>
     <string>com.alibaba.beehive</string>
     <key>moduleClasses</key>
     <array>
         <dict>
             <key>moduleClass</key>
             <string>HomeModule</string>
             <key>moduleLevel</key>
             <integer>1</integer>
             <key>modulePriority</key>
             <string>600</string>
         </dict>
         <dict>
             <key>moduleClass</key>
             <string>TMTradeAdapter</string>
             <key>moduleLevel</key>
             <integer>1</integer>
             <key>modulePriority</key>
             <string>599</string>
         </dict>
     </array>
</dict>
</plist>

在AppDelegate中 [[BeeHive shareInstance] setContext:[BHContext shareInstance]]; 设置BHContext时就会注册plist中的module。

- (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<NSDictionary *> *modulesArray = [moduleList objectForKey:kModuleArrayKey];
    NSMutableDictionary<NSString *, NSNumber *> *moduleInfoByClass = @{}.mutableCopy;
    [self.BHModuleInfos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [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];
        }
    }];
}

将Plist中的module加入到BHModuleInfos中。

Load方法注册

// BeeHive
+ (void)load
{
    [BeeHive registerDynamicModule:[self class]];
}

+ (void)registerDynamicModule:(Class)moduleClass
{
    [[BHModuleManager sharedManager] registerDynamicModule:moduleClass];
}

// BHModuleManager
- (void)registerDynamicModule:(Class)moduleClass
{
    [self registerDynamicModule:moduleClass shouldTriggerInitEvent:NO];
}

Load方法最终是调用BHModuleManager中的registerDynamicModule:方法来处理,该方法已在上一节中说明。

+load的方式可以使用BH_EXPORT_MODULE宏来替代完成。

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

BH_EXPORT_MODULE宏中传入了参数isAsync,代表是否异步加载Module模块。如果是YES,则表示需要异步加载,NO则表示同步加载。

回过头来看看AppDelegate中setContext方法中的操作:

// AppDelegate
[[BeeHive shareInstance] setContext:[BHContext shareInstance]];

// BeeHive
-(void)setContext:(BHContext *)context {
    _context = context;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self loadStaticServices];
        [self loadStaticModules];
    });
}

- (void)loadStaticModules {
    [[BHModuleManager sharedManager] loadLocalModules];
    [[BHModuleManager sharedManager] registedAllModules];
}

重点关注一下registedAllModules方法 :

- (void)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:初始化所有的module
    [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 removeAllObjects];
    [self.BHModules addObjectsFromArray:tmpArray];
    //给module对象注册系统事件
    [self registerAllSystemEvents];
}

在经历registedAllModules方法之后,所有注册的module都生成了对应的实例对象。

注意:

  1. 所有的Module的对象都要是遵守BHModuleProtocol协议的。
  2. Module不能在任何其他地方alloc创建出来,即使创建一个新的Module实例出来,它也并不在BHModuleManager的管理下,无法接收BHModuleManager分发的系统事件。

BeeHive模块间调用

通过处理Event编写各个业务模块可以实现插件化编程,各业务模块之间没有任何依赖,core与module之间通过event交互,实现了插件隔离。但有时候我们需要模块间的相互调用某些功能来协同完成功能。目前模块间的调用使用基于接口的实现Service访问方式(Java spring框架实现)。基于接口Service访问的优点是可以编译时检查发现接口的变更,从而及时修正接口问题。缺点是需要依赖接口定义的头文件,通过模块增加得越多,维护接口定义的也有一定工作量。

模块间调用的协议都是通过BHServiceManager来维护的。

BeeHive提供了三种方式来注册协议,这里和module的注册方式相同:Annotation方式注册、读取本地plist方式注册、API注册。

Annotation方式注册

使用@BeeHiveService进行Annotation标记。BeeHiveService的宏定义如下:

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

#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname"")))

在示例中,@BeeHiveService(HomeServiceProtocol,BHViewController)在预编译结束后会完全展开成如下所示:

@class BeeHive; char * kHomeServiceProtocol_service __attribute((used, section("__DATA,""BeehiveServices"""))) = "{ \"""HomeServiceProtocol""\" : \"""BHViewController""\"}”;

这里类比注册module时的Annotation解析,也是把数据存在特殊的段内,具体的原理可以参考注册module的分析。

同理,通过调用函数BHReadConfiguration读取之前注册到特殊段BeehiveServices中的数据,这里是如下所示的json字符串,{协议字符串:实现该协议的class类名字符串},如下所示:

{"HomeServiceProtocol":"BHViewController”}

读取到数据后,进行service的注册:

static void dyld_callback(const struct mach_header *mhp, intptr_t vmaddr_slide)
{
    //register module
...
    
    //register services
    NSArray<NSString *> *services = BHReadConfiguration(BeehiveServiceSectName,mhp);
    for (NSString *map in services) {
        NSData *jsonData =  [map dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error = nil;
        id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
        if (!error) {
            if ([json isKindOfClass:[NSDictionary class]] && [json allKeys].count) {
                
                NSString *protocol = [json allKeys][0];
                NSString *clsName  = [json allValues][0];
                
                if (protocol && clsName) {
                    [[BHServiceManager sharedManager] registerService:NSProtocolFromString(protocol) implClass:NSClassFromString(clsName)];
                }
                
            }
        }
    }
}

- (void)registerService:(Protocol *)service implClass:(Class)implClass
{
    NSParameterAssert(service != nil);
    NSParameterAssert(implClass != nil);
    
    if (![implClass conformsToProtocol:service]) {
        if (self.enableException) {
            @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ module does not comply with %@ protocol", NSStringFromClass(implClass), NSStringFromProtocol(service)] userInfo:nil];
        }
        return;
    }
    
    //协议是否已经注册
    if ([self checkValidService:service]) {
        if (self.enableException) {
            @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil];
        }
        return;
    }
    
    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];
    }
}

在注册协议前会有两个检查registerService:implClass::

  • 检查implClass是否遵循了service
  • 检查service协议是否已经注册

如果两个条件有一个没有检查通过,则会抛出异常。

如果条件通过,则会在allServicesDict中加入键值对,{NSStringFromProtocol(service):NSStringFromClass(implClass)}

读取本地Plist方式注册

读取本地的plist文件之前,需要先设置plist文件路径。

//AppDelegate.m
[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService”;

BeeHive的配置都可以通过BHContext进行传递。

plist中的数据格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>service</key>
        <string>UserTrackServiceProtocol</string>
        <key>impl</key>
        <string>BHUserTrackViewController</string>
    </dict>
</array>
</plist>

注册plist中service的时机同注册plist管理的module类似:

// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    
    [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"];
    
    [super application:application didFinishLaunchingWithOptions:launchOptions];
    
...
    
    return YES;
}

// BeeHive.m
-(void)setContext:(BHContext *)context
{
    _context = context;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self loadStaticServices];
        [self loadStaticModules];
    });
}

-(void)loadStaticServices
{
    [BHServiceManager sharedManager].enableException = self.enableException;
    
    [[BHServiceManager sharedManager] registerLocalServices];
    
}

注册service的具体实现如下:

- (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];
}

注册完成之后,allServicesDict中的值如下:

{@"HomeServiceProtocol" : @"BHViewController", @"UserTrackServiceProtocol" : @"BHUserTrackViewController"}

注意:NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist”]; 这段代码,如果主工程Podfile中未使用了use_frameworks!,则可以正常获取到plist,如果使用了use_frameworks!,则得使用其他方式获取,具体的方案请google。

API注册

API注册service使用的api是BeeHive的接口- (void)registerService:(Protocol *)proto service:(Class)serviceClass, 该接口内部实现也是调用BHServiceManagerregisterService:implClass:接口。

// BeeHive.m
- (void)registerService:(Protocol *)proto service:(Class)serviceClass {
    [[BHServiceManager sharedManager] registerService:proto implClass:serviceClass];
}

例如:[[BeeHive shareInstance] registerService:@protocol(TradeServiceProtocol) service:[BHTradeViewController class]];, 该代码可以放置在module的 modInit:方法内部或者modSetup:方法内部,具体可以查看使用文档或者demo。

注意:BHMSetupEventBHMInitEvent事件都会在项目的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions函数内部触发。

BHServiceManager中的allServicesDict包含了所有方式注册的service。

与module注册相比,service的注册没有对实现协议的对象进行初始化。只是将协议和实现协议的对象之间的这种关联关系存储和维护,而在module的注册过程中,对所有注册的module进行实例的初始化。

因此,在BHServiceManager的公共接口中,有一组createService:接口用于访问实现协议的对象。

- (id)createService:(Protocol *)service
{
    return [self createService:service withServiceName:nil];
}

- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName {
    return [self createService:service withServiceName:serviceName shouldCache:YES];
}

- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache {
    if (!serviceName.length) {
        serviceName = NSStringFromProtocol(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 = serviceName;
    if (shouldCache) {
        id protocolImpl = [[BHContext shareInstance] getServiceInstanceFromServiceName:serviceStr];
        if (protocolImpl) {
            return protocolImpl;
        }
    }
    
    Class implClass = [self serviceImplClass:service];
    if ([[implClass class] respondsToSelector:@selector(singleton)]) {
        if ([[implClass class] singleton]) {
            if ([[implClass class] respondsToSelector:@selector(shareInstance)])
                implInstance = [[implClass class] shareInstance];
            else
                implInstance = [[implClass alloc] init];
            if (shouldCache) {
                [[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
                return implInstance;
            } else {
                return implInstance;
            }
        }
    }
    return [[implClass alloc] init];
}

从上面的实现中可以看出:

  • service对象存在单例和多实例的区别。
  • 如果BHServiceProtocol协议对象实现了singleton返回YES,且shouldCache入参值是YES,则通过createService:获取的对象为单例对象,如果singleton方法返回的是NO,则每次调用createService:都会创建一个新的对象。
  • 这里的单例存在线程安全问题。如果开发者将BHServiceProtocol协议对象实现了singleton返回YES,且shareInstance方法的实现是返回单例对象,则不管shouldCache的入参值是YES还是NO,createService:都会是同一个实例。但是,如果开发者将shareInstance方法的实现只是返回对象(非单例),即使singleton返回YES,shouldCache入参值是YES,也可能存在多个实例的情况。

上下文环境Context

BeeHive中使用BHContext初始化设置应用的项目信息,并在各模块间共享整个应用程序的信息。例如:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [BHContext shareInstance].env = BHEnvironmentDev; //定义应用的运行开发环境
    [BHContext shareInstance].application = application;
    [BHContext shareInstance].launchOptions = launchOptions;
    [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/CustomModulePlist";//可选,默认为BeeHive.bundle/BeeHive.plist
    [BHContext shareInstance].serviceConfigName =  @"BeeHive.bundle/CustomServicePlist";//可选,默认为BeeHive.bundle/BHService.plist
    [[BeeHive shareInstance] setContext:[BHContext shareInstance]];

    [super application:application didFinishLaunchingWithOptions:launchOptions];

...

    return YES;
}

//BHContext.h

@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;

@end
  • 应用的运行开发环境
  • 应用启动信息:application,launchOptions
  • module和service的plist配置信息
  • 缓存的service
  • 3D-Touch, OpenURL moduel, Remote Or Local Notifications, User Activity Model等

最后,附一张BeeHive主要类的的类图关系。

《阿里组件化框架BeeHive解析》 BeeHive类图

如果觉得本文对你有帮助,就请用微信随意打赏我吧_

《阿里组件化框架BeeHive解析》 wechat_appreciate_qrcode.png

点赞