美文网首页
BeeHive 框架实现原理分析

BeeHive 框架实现原理分析

作者: 飞到哪 | 来源:发表于2017-03-14 23:18 被阅读0次

    BeeHive是什么?
    BeeHive是阿里提出的在iOS平台上实现模块化编程的一套实现方案。模块化、组件化是目前在客户端发展的热点方向之一,在业务快速变化、迭代的今天,模块化编程可以有效的降低代码的耦合性,当开发人员面对业务代码不断臃肿的App而烦恼时,模块化编程为其指出了一条新的道路。从BeeHive开源的代码来看,BeeHive的代码显得非常精简,甚至可以说有些简陋,但其却提供了一种极其简化的模块设计方法,正所谓“重剑无锋,大巧不工”,在代码日益复杂的今天,回归简单提供了一种新的想法。
    Module与Service
    在BeeHive当中最重要的两个概念是Module 与Service,对应成中文可以翻译成“模块”与“服务”。Module是有生命周期的,其调用的时机是由事件来触发的,事件包括了系统事件,通用事件以及自定义事件。对于Module而言,BeeHive实现了队列化回调Module方法,开发人员可以给某个系统事件注册多个Module,BeeHive会依次调用不同的Module处理方法。Service则是单纯的代码实现模块,可以是实现某个功能的代码或业务代码,Service是没有生命周期的,在任何时候都可以调用。BeeHive借鉴了Spring的Service设计思想,Module与Service需要对接口进行抽象,通过接口来关联两个不同的模块,从而最大的降低代码的耦合性。

    Paste_Image.png

    模块调用关系
    Module与Service的设计可以覆盖绝大多数的客户端的开发场景,在某个事件触发的时候进行某些处理,或者完全由用户触发某个功能调用。例如,在BeeHive当中实现了AppDelgate回调接口的Module化。AppDelgate是整个系统回调入口,在传统的App开发当中,需要在该类当中不同的回调函数写入大量的处理方法,例如像推送的注册、整个应用资源的初始化等,这会导致AppDelgate当中的代码非常臃肿。AppDelgate的Module化则彻底改变了这些,开发员只需要实现BHModuleProtocol协议,在相应的回调接口当中实现相应的处理。例如,可以实现一个推送注册的APNsModule了,该APNsModule只实现了推送功能的注册。开发人员只需要将Module注册即可,目前BeeHive支持文件形式的Module注册及宏定义的注册方式。在未来如果需要对推送进行升级时,只需要直接替换这个APNsModule类即可,对AppDelgate内部的代码不需要做任何修改。
    下面是AppDelgate Module化的事件触发机制,在AppDelegate的回调接口didLaunch当中会依次调用Module 的ModSetup、ModInit以及ModSplash方法。


    BeeHiveMod事件驱动

    BeeHive实现原理
    BeeHive提供了一个BHAppDelegate类,使用BeeHive必须继承该BHAppDelgate类,自己创建一个子类,在BeeHive创建的示例代码当中,子类需要重写

    - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    

    方法,如下所示

    - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    {
    [BHContextshareInstance].application= application;
    [BHContextshareInstance].launchOptions= launchOptions;
    [BHContextshareInstance].moduleConfigName=@"BeeHive.bundle/BeeHive";//可选,默认为BeeHive.bundle/BeeHive.plist
    [BHContextshareInstance].serviceConfigName=@"BeeHive.bundle/BHService";
    [BeeHiveshareInstance].enableExpection=YES;
    [[BeeHiveshareInstance]setContext:[BHContextshareInstance]];
    [[BHTimeProfilersharedTimeProfiler]recordEventTime:@"BeeHive::super start launch"];
    [superapplication:applicationdidFinishLaunchingWithOptions:launchOptions];
    id homeVc = [[BeeHiveshareInstance]createService:@protocol(HomeServiceProtocol)];
    if([homeVcisKindOfClass:[UIViewControllerclass]]) {
    UINavigationController*navCtrl = [[UINavigationControlleralloc]initWithRootViewController:(UIViewController*)homeVc];
    navCtrl.navigationBarHidden=YES;
    [homeVcsetIsNavbarHidden:YES];
    self.window= [[UIWindowalloc]initWithFrame:[UIScreenmainScreen].bounds];
    self.window.rootViewController= navCtrl;
    [self.windowmakeKeyAndVisible];
    }
    returnYES;
    }
    

    在[superapplication:applicationdidFinishLaunchingWithOptions:launchOptions];前面这些是实现BeeHive初始化的核心代码。BHContext提供了上下文的环境参数,系统的回调接口可以通过BHContext对象来获取相关的参数。开始几行代码是初始化context几个基本的环境变量。其定义了module与service初始化的文件路径。BeeHive是实现Module与Service注册的核心类。其内部实现的逻辑会分别从文件及宏定义当中读取Module及Service的映射关系,代码如下所示:

    -(void)setContext:(BHContext*)context
    {
    _context= context;
    staticdispatch_once_tonceToken;
    dispatch_once(&onceToken, ^{
    [selfloadStaticServices];
    [selfloadStaticModules];
    });
    }
    

    文件注册Module及Service的示例如下:

    Module文件注册

    Service文件注册

    Module宏注册

    Service宏注册

    文件注册的方法相对比较简单,宏定义注册则看起来有点迷糊,下面来分析一下其中的原理。打开宏定义可以看到下面的代码:

    #ifndef BeehiveModSectName
    #define BeehiveModSectName"BeehiveMods"
    #endif
    #ifndef BeehiveServiceSectName
    #define BeehiveServiceSectName"BeehiveServices"
    #endif
    #define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))
    #define BeeHiveMod(name) \
    char * k##name##_mod BeeHiveDATA(BeehiveMods) =""#name"";
    #define BeeHiveService(servicename,impl) \
    char * k##servicename##_service BeeHiveDATA(BeehiveServices) ="{ \""#servicename"\" : \""#impl"\"}";
    </pre>
    宏会定义一个全局的变量,并将其放入在__DATA 的对应的sectname当中,Module会放在BeehiveMods当中,Service会放在BeehiveServices当中。在代码BHAnnotation.m文件当中
    <pre>
    staticNSArray* BHReadConfiguration(char*section)
    {
    NSMutableArray*configs = [NSMutableArrayarray];
    Dl_infoinfo;
    dladdr(BHReadConfiguration, &info);
    #ifndef __LP64__
    //const struct mach_header *mhp = _dyld_get_image_header(0); // both works as below line
    conststructmach_header *mhp = (structmach_header*)info.dli_fbase;
    unsignedlongsize =0;
    uint32_t *memory = (uint32_t*)getsectiondata(mhp,"__DATA", section, & size);
    #else/* defined(__LP64__) */
    conststructmach_header_64*mhp = (structmach_header_64*)info.dli_fbase;
    unsignedlongsize =0;
    uint64_t*memory = (uint64_t*)getsectiondata(mhp,"__DATA", section, & size);
    #endif/* defined(__LP64__) */
    for(intidx =0; idx < size/sizeof(void*); ++idx){
    char*string = (char*)memory[idx];
    NSString*str = [NSStringstringWithUTF8String:string];
    if(!str)continue;
    BHLog(@"config = %@", str);
    if(str) [configsaddObject:str];
    }
    returnconfigs;
    }
    

    该段代码会读取传入的sectname段的数据,根据其中的内容找到Module及service的对应关系。可以用otool查看可执行文件数据段的内容,如下所示,其中有两个char*指针,各占8字节。

    BeeHiveMods section内容

    BeeHive带来的启发
    客户端在公司业务发展的过程中体积越来越庞大,其中堆叠了大量的业务逻辑代码,不同业务模块的代码相互调用,相互嵌套,代码之间的耦合性越来越高,调用逻辑会越来越混乱。当某个模块需要升级的时候,往往会有牵一发而动全身的感觉。特别是如果工程最初设计的时候没有考虑的接口的封装,而将大量的业务代码与功能模块代码混在一起时,将来的升级往往需要手动对代码的进行修改及调整,带来的工作量是非常巨大的。模块化、组件化可以将代码的功能逻辑尽量封装在一起,对外只提供接口,业务逻辑代码与功能模块通过接口进行弱耦合。这样在未来替换模块时,只需要动态注册接口与类的对应关系即可。BeeHive提供了很好的设计思路。另外由于我在公司属于基础框架组,为其它项目组提供基础功能组件使用。其它项目组在使用时,往往将直接将业务代码堆叠在我们框架的代码上,导致在升级上非常麻烦,经常需要手动升级。BeeHive的Module设计是一种非常好的想法,我可以自定义一些事件,对一些组件的执行逻辑像AppDelgate一样进行改造,例如像webView,功能逻辑以Module进行注册使用,这样可以将功能Module化,对其它项目组的使用更加规范。

    相关文章

      网友评论

          本文标题:BeeHive 框架实现原理分析

          本文链接:https://www.haomeiwen.com/subject/ocuknttx.html