为什么运用 BeeHive

在狼人杀项目时,iOS 团队有 9 个人协作开发,假设没有一个好的模块化计划支撑,诸如代码冲突 / 逻辑复用等问题将占用不少开发时刻,必然面临功率问题。当时的解决计划,就运用了类似 BeeHive 的模块化计划进行团队开发。

事务模块化的优点:

  • 保护便利,区分事务模块,事务逻辑在一个模块内。
  • 代码复用,类似度高的模块能够低本钱迁移到其它项目。
  • 易于扩展,事务解耦使得新功用的开发负担小。

什么时候应进行模块化

一套结构肯定有学习和运用本钱,这儿涉及一个产出和投入的问题。那么怎么具体的衡量是否运用呢?

个人愚见,有以下状况是值得去考虑做模块化的:

  • 事务规模,一是事务代码本身的体量大,二是事务发展速度,事务增速快,有必要考虑行模块化,适应日后复杂景象。
  • 团队规模,团队人数也是事务规模的表现,除代码提交的冲突增加,交流本钱也直线上升,需求更具功率的协同。

模块化的规范是什么

目前业界并没有构成规范共识,一定之规。

而一定要量化规范的话,个人的浅显实践认为是:

事务模块数<=研制人员数*3

假设模块化造成了负效果,违反咱们让事务跑得更好更快的初衷,那便是舍本求末了。

BeeHive 是什么

BeeHive 是阿里巴巴开源的一款模块化结构,依照官方的介绍:

BeeHive 是用于 iOSApp 模块化编程的结构完结计划,吸收了 Spring 结构 Service 的理念来完结模块间的API耦合。

架构图:

模块化方案 - BeeHive

与上面的架构图所对应的,BeeHive 重点重视的有两个部分:

  • 事务模块
    • 模块怎么注册
    • 模块怎么分发系统事情&运用事情
  • 服务
    • 服务怎么注册
    • 服务怎么被模块调用

BeeHive 怎么完结模块化

BeeHive 模块注册

具体进行事务开发时, 一切事务模块 Module 一致交由 BeeHive 来进行办理,怎么将 Module 注册到 BeeHive 是关键。

BeeHive 提供了 3 种办法来进行注册:

  • 动态注册
  • 静态注册
  • Annotation

BeeHive 结构在模块解耦方面,比较特别的是 Annotaition 办法。

动态注册
动态注册运用

动态注册一个 NewModule 的代码如下:

#import "BeeHive.h"
@interface NewModule()<BHModuleProtocol>
@end
@implementation NewModule
//假设参数为 YES 代表发动之后异步履行模块的初始化,为 NO 则同步履行
BH_EXPORT_MODULE(NO)
@end
动态注册完结

动态注册运用需求利用宏 BH_EXPORT_MODULE

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

能够发现,上面的注册机遇是固定在 load 办法的。

关于模块的动态注册,调用完结如下:

模块化方案 - BeeHive

最后会走到 -[BHModuleManager addModuleFromObject:shouldTriggerInitEvent:] 完结:

- (void)addModuleFromObject:(id)object
     shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
    Class class;
    NSString *moduleName = nil;
    if (object) {
        class = object;
        //取出 module 类名字符串
        moduleName = NSStringFromClass(class);
    } else {
        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;
    }
    if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {//查看是否遵从相关 BHModuleProtocol 协议
        NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];
        BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];
        int levelInt = 1;
        if (responseBasicLevel) {
            levelInt = 0;
        }
        //设置 moudle 字典信息
        [moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
        if (moduleName) {
            [moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
        }
        [self.BHModuleInfos addObject:moduleInfo];
        //将模块初始化,存进 BHModules 数组
        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) {// BHModules 依据优先级进行排序
            // 默许两个模块都是用 BHModuleNormal=1
            NSNumber *module1Level = @(BHModuleNormal);
            NSNumber *module2Level = @(BHModuleNormal);
            // 假设模块完结了 basicModuleLevel 办法, BHModuleBasic=0
            if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) {
                module1Level = @(BHModuleBasic);
            }
            if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) {
                module2Level = @(BHModuleBasic);
            }
            if (module1Level.integerValue != module2Level.integerValue) {//两个模块 basicModuleLevel 不相等
                // BHModuleBasic 的模块会排在 BHModuleNormal 
                return module1Level.integerValue > module2Level.integerValue;
            } else {//两个模块 basicModuleLevel 相等
                NSInteger module1Priority = 0;
                NSInteger module2Priority = 0;
                if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) {
                    module1Priority = [moduleInstance1 modulePriority];
                }
                if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) {
                    module2Priority = [moduleInstance2 modulePriority];
                }
                //比较两个模块的 modulePriority ,看哪一个值大就在前面 
                return module1Priority < module2Priority;
            }
        }];
        //将模块的实例和 BeeHive 事情,进行对应的注册
        [self registerEventsByModuleInstance:moduleInstance];
        if (shouldTriggerInitEvent) {//是否需求分发初始化事情,动态注册 registerDynamicModule 过来的参数是 NO ,不做分发
            [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];
            });
        }
    }
}
静态注册
静态注册运用

首要,静态注册要在装备中 指定 plist 文件名

[BHContext shareInstance].moduleConfigName = @"BHModule";

然后在 Plist 文件里边装备好 module 对应的信息:

模块化方案 - BeeHive
  • moduleClass: 模块类的类名。
  • moduleLevel: 假设不去设置 Level 默许是 BHModuleNormal。 只会有两种 Level ,BHModuleBasic = 0 ,BHModuleNormal = 1。
  • modulePriority: 对应 BHModuleProtocol 的同名办法,moduleLevel 相等状况下比较 modulePriority 。
静态注册完结
模块化方案 - BeeHive

重点重视 2 个办法:

  • -[BHModuleManager loadLoacalModules] 从 plist 文件扫描出来对应模块字典信息:
- (void)loadLocalModules
{
    //依据 [BHContext shareInstance].moduleConfigName 拿到文件路径
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
        return;
    }
    NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
    //plist 里的的静态 module 数组
    NSArray<NSDictionary *> *modulesArray = [moduleList objectForKey:kModuleArrayKey];
    //初始化一个存储 self.BHModuleInfos 已有的 moudule 类名的字典
    NSMutableDictionary<NSString *, NSNumber *> *moduleInfoByClass = @{}.mutableCopy;
    [self.BHModuleInfos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [moduleInfoByClass setObject:@1 forKey:[obj objectForKey:kModuleInfoNameKey]];
    }];
    //遍历 plist 里的的静态 module 数组
    [modulesArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (!moduleInfoByClass[[obj objectForKey:kModuleInfoNameKey]]) {//假设是没有在 self.BHModuleInfos 的 module 类,则把对应的字典增加进取
            [self.BHModuleInfos addObject:obj];
        }
    }];
}
  • -[BHModuleManager registedAllModules] 依据模块信息,实例化静态注册的模块类:
- (void)registedAllModules
{
    // self.BHModuleInfos 排序一次
    [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) {//对没有进行实例化的 module 进行初始化
            id<BHModuleProtocol> moduleInstance = [[moduleClass alloc] init];
            [tmpArray addObject:moduleInstance];
        }
    }];
    //增加到模块数组
    [self.BHModules addObjectsFromArray:tmpArray];
    //将模块与对应事情进行注册
    [self registerAllSystemEvents];
}

在经历registedAllModules办法之后,一切注册的module都生成了对应的实例目标。

PS:假设对模块顺序有要求时要注意,动态注册的模块,整体的顺序会排在静态注册的前面,由于 load 办法的机遇在更前面。

Annotation 注册模块
Annotation 注册运用

模块假设运用 Annotaiton 办法注册,代码十分的简略:

#import <BHModuleProtocol.h>
@BeeHiveMod(YourModule)
@interface YourModule() <BHModuleProtocol>
@end
Annotation 注册完结

查看 BeeHiveMod() 的宏界说:

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

其中的 BeeHiveDATA 也是一个宏:

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

直接打开变成:

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

Annotation 相关扩展

编译特点 __attribute__

这儿的关键是 __attribute__ ,日常开发中不少场景也有它的身影,它的运用格局也很简略:

__attribute__((特点列表))

例如作为 办法过期的正告 对已抛弃办法运用:

模块化方案 - BeeHive

完结效果:

模块化方案 - BeeHive

查看相关的 官方解释:

An attribute specifier is of the form __attribute__ ((attribute-list)). An attribute list is a possibly empty comma-separated sequence of attributes, where each attribute is one of the following:

  • Empty. Empty attributes are ignored.
  • An attribute name (which may be an identifier such as unused, or a reserved word such as const).
  • An attribute name followed by a parenthesized list of parameters for the attribute. These parameters take one of the following forms:
    • An identifier. For example, mode attributes use this form.
    • An identifier followed by a comma and a non-empty comma-separated list of expressions. For example, format attributes use this form.
    • A possibly empty comma-separated list of expressions. For example, format_arg attributes use this form with the list being a single integer constant expression, and alias attributes use this form with the list being a single string constant.

大意是说的 __attribute__ ((attribute-list)) 运用的 attribute-list 特点列表有三种状况:

  • 为空,空的会被疏忽。
  • 单纯的特点名,例如 unused 这样的:__attribute__((unused))
  • 特点名带参数运用:
    • 特定的符号,例如 : __attribute__((mode(DI)))
    • 特定的符号,可能是逗号分割的表达式。例如 : __attribute__((format(printf, a, b)))
    • 一个可能为空的逗号分隔的表达式列表。例如: __attribute__((noreturn, format(printf, 1, 2)) );

而在 BeeHive 傍边运用到的代码则是:

__attribute((used, section("__DATA,"BeehiveMods" ")))

其实便是别离运用了 2 个关键字,一个关键字是 used,另外一个则是 section()

used

used 具体描述见 gun 官方文档 :

used

This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced.

When applied to a static data member of a C++ class template, the attribute also means that the member is instantiated if the class itself is instantiated.

假设 used 比较生疏的话,那 unused 的正告,作为 iOS 开发者咱们应该很熟悉了。

关于 __attribute((used)) 它的效果与 unused 相反, 是向编译器说明这段代码有用,即便在没有用到的状况下编译器也不会正告。

代码假设没有加 used 变量会被编译器给优化了。

section

要了解 section ,要知道 Mach-O 的构成:

模块化方案 - BeeHive

Mach-O 首要由 3 部分组成:

  • Mach-O 头(Mach Header):Mach Header 描述了 Mach-O 的 CPU 架构、文件类型以及加载指令等信息。
  • 加载指令(Load Commands):Load Commands 描述了文件中数据的具体安排结构,不同的数据类型运用不同的加载指令表明。
  • 数据区(Data):Data 中每一个段(Segment)的数据都保存在此,段的概念和 ELF 文件中段的概念类似,都具有一个或多个 Section ,用来存放数据和代码。

作为与 Mach-O 文件格局有关的 Segment 的界说,能够在 /usr/include/mach-o/loader.h 中看到:

#define	SEG_PAGEZERO	"__PAGEZERO"	/* the pagezero segment which has no */
					/* protections and catches NULL */
					/* references for MH_EXECUTE files */
#define	SEG_TEXT	"__TEXT"	/* the tradition UNIX text segment */
#define	SEG_DATA	"__DATA"	/* the tradition UNIX data segment */
#define	SEG_OBJC	"__OBJC"	/* objective-C runtime segment */

而关于 section 的结构也能够看到:

struct section { /* for 32-bit architectures */
	char		sectname[16];	/* name of this section */
	char		segname[16];	/* segment this section goes in */
	uint32_t	addr;		/* memory address of this section */
	uint32_t	size;		/* size in bytes of this section */
	uint32_t	offset;		/* file offset of this section */
	uint32_t	align;		/* section alignment (power of 2) */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
};
struct section_64 { /* for 64-bit architectures */
	char		sectname[16];	/* name of this section */
	char		segname[16];	/* segment this section goes in */
	uint64_t	addr;		/* memory address of this section */
	uint64_t	size;		/* size in bytes of this section */
	uint32_t	offset;		/* file offset of this section */
	uint32_t	align;		/* section alignment (power of 2) */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
	uint32_t	reserved3;	/* reserved */
};

了解 section 的界说,就很容易了解__attribute((section("__DATA,"BeehiveMods" "))) 的意思,便是将数据放进 segname__DATA , sectnameBeehiveMods section 中。

读取 section

在 BeeHive 中既然把数据存进 section,天然也需求将 section 存储的数据取出来。

这儿的关键在于 BHReadConfiguration 办法:

NSArray<NSString *>* BHReadConfiguration(char *sectionName,const struct mach_header *mhp)
{
    NSMutableArray *configs = [NSMutableArray array];
    unsigned long size = 0;
    // 利用 getsectiondata 办法取出数据
#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){//遍历获取 section 中存储的字符串
        char *string = (char*)memory[idx];
        NSString *str = [NSString stringWithUTF8String:string];
        if(!str)continue;
        BHLog(@"config = %@", str);
        if(str) [configs addObject:str];
    }
    return configs;
}

利用 constructor 完结模块加载

能够看到代码中有一个 initProphet 函数的代码如下:

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

其中的玄机就在 __attribute__((constructor)) ,它到效果是系统在履行 main() 函数之前调用该函数 。

官方解释如下:

constructor destructor

The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () has completed or exit () has been called. Functions with these attributes are useful for initializing data that will be used implicitly during the execution of the program.

These attributes are not currently implemented for Objective-C.

感兴趣的能够去看看相关文档和资料 attribute((constructor))用法解析 ,这儿不再打开。

_dyld_register_func_for_add_image() 则表明每一次镜像的加载,都会触发传入的回调函数。

在 BeeHive 的上述代码里,每次 App 发动都会触发 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 *> *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)];
                }
            }
        }
    }
}

回调函数的首要效果,便是每次发动,去注册模块和服务的数据信息。

BeeHive 模块分发事情

将模块注册到 BeeHive 后,就能够接收到事情:

  • UIApplicationDelegate 系统事情
  • BeeHive 模块事情
  • 事务自界说事情

BHModuleProtocol 界说事情

上述的 3 种事情,都在 BHModuleProtocol 傍边界说了办法,Module 类可依据需求去完结:

@protocol BHModuleProtocol <NSObject>
@optional
...
- (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;
- (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;
- (void)modDidCustomEvent:(BHContext *)context;
@end

模块事情分发完结

BHAppDelegate 替换

main 函数进口,要替换 AppDelegate 为 BHAppDelegate 或许 BHAppDelegate 的子类,是运用 BeeHive 的一个条件:

int main(int argc, char * argv[]) {
  NSString * appDelegateClassName;
  @autoreleasepool {
    appDelegateClassName = NSStringFromClass([BHAppDelegate class]);
  }
  return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

这样才能让事务模块收到 BeeHive 分发对应的事情。

BHAppDelegate 完结监听

如下代码,在 BHAppDelegate 中,触发 系统事情 时,由 BHModuleManager 分发 BeeHive 事情到模块傍边:

@implementation 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];
    });
...    
    return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMWillResignActiveEvent];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [[BHModuleManager sharedManager] triggerEvent:BHMDidEnterBackgroundEvent];
}
...
@end

能够看到,上述监听都收拢到了 -[BHModuleManager triggerEvent:] 调用傍边。

BHModuleManager 事情分发逻辑
模块化方案 - BeeHive

除了 eventType 为 BHMInitEvent / BHMTearDownEvent 时特殊,其它的处理办法都相同。

defalut 默许的模块办法履行完结流程如下图:

模块化方案 - BeeHive
BHMInitEvent

BeeHive 模块 初始化 事情:

- (void)handleModulesInitEventForTarget:(id<BHModuleProtocol>)target
                        withCustomParam:(NSDictionary *)customParam
{
    ...
    [moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
        __weak typeof(&*self) wself = self;
        void ( ^ bk )(void);
        //界说 bk() 
        bk = ^(){
            __strong typeof(&*self) sself = wself;
            if (sself) {
                //判别 module 类实例是否响应 modInit 办法,并进行回调
                if ([moduleInstance respondsToSelector:@selector(modInit:)]) {
                    [moduleInstance modInit:context];
                }
            }
        };
				...
        // 关于 async 办法的响应判别,再决议怎么履行 bk()
        if ([moduleInstance respondsToSelector:@selector(async)]) {
            BOOL async = [moduleInstance async];
            if (async) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    bk();
                });
            } else {
                bk();
            }
        } else {
            bk();
        }
    }];
}
BHMTearDownEvent

BeeHive 模块 毁掉 事情:

- (void)handleModulesTearDownEventForTarget:(id<BHModuleProtocol>)target
                            withCustomParam:(NSDictionary *)customParam
{
    ...
    //数组倒序循环,依次对 module 类实例调用 modTearDown 
    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];
        }
    }
}

BHMTearDownEvent 事情特别的是,按优先级从低到高毁掉。

事务自界说事情

BeeHive 还提供了自界说事情的办法来进行分发。

自界说事情分发
-(void)someMethod
{
    //调用 tiggerCustomEvent 分发自界说事情 BHMDidCustomEvent 
    [[BHModuleManager sharedManager] tiggerCustomEvent:BHMDidCustomEvent];
}

-[BHModuleManager tiggerCustomEvent:]办法的完结为:

- (void)tiggerCustomEvent:(NSInteger)eventType
{
    if(eventType < 1000) {//判别是否为小于 1000 的自界说事情
        return;
    }
    [[BHModuleManager sharedManager] triggerEvent:eventType];
}

判别 eventType 后,直接透传给 BHModuleManager 进行处理。

自界说事情 – 手动注册

假设界说好的枚举值 BHMDidCustomEvent 无法满意需求,也运用满意条件(大于1000)的事情值:

//Module 
-(void)modInit:(BHContext *)context
{
    //注册 10404 的事情
    [[BHModuleManager sharedManager] registerCustomEvent:10404
                                      withModuleInstance:self
                                          andSelectorStr:NSStringFromSelector(@selector(eventForCustom))];
}

具体完结:

- (void)registerCustomEvent:(NSInteger)eventType
   withModuleInstance:(id)moduleInstance
       andSelectorStr:(NSString *)selectorStr {
    if (eventType < 1000) {//判别是否为小于 1000 的自界说事情
        return;
    }
    [self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr];
}
- (void)registerEvent:(NSInteger)eventType
   withModuleInstance:(id)moduleInstance
       andSelectorStr:(NSString *)selectorStr {
    //生成 SEL
    SEL selector = NSSelectorFromString(selectorStr);
    if (!selector || ![moduleInstance respondsToSelector:selector]) {
        return;
    }
    //将 eventType 注册进字典 BHSelectorByEvent,经过 eventType 绑定 SEL           
    NSNumber *eventTypeNumber = @(eventType);
    if (!self.BHSelectorByEvent[eventTypeNumber]) {
        [self.BHSelectorByEvent setObject:selectorStr forKey:eventTypeNumber];
    }
    //将 eventType 注册进字典 BHModulesByEvent           
    if (!self.BHModulesByEvent[eventTypeNumber]) {
        [self.BHModulesByEvent setObject:@[].mutableCopy forKey:eventTypeNumber];
    }
    /**
    1.判别 eventType 对应的模块实例列表 eventModules 是否包含 moduleInstance 
    2.1 eventModules 没有 moduleInstance ,则增加 moduleInstance
    2.2 对增加完目标的 eventModules 按优先级排序 
    */      
    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;
            }
        }];
    }
}

BeeHive 模块间 Service

BeeHive 的模块间经过 Service 调用,来协同其它模块完结功用。

Service 是什么

这儿的 Service 实际上指 Protocol,且都需求遵从 BHServiceProtocol ,官方比如示例 HomeServiceProtocol

@protocol HomeServiceProtocol <NSObject, BHServiceProtocol>
-(void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName;
@end

Protocol 的优点是,不同的模块,不会再依靠某一个类实例,而是 依靠接口,关于运用者屏蔽具体完结。

Service 注册

ServiceProtocol 有必要注册,才能由 BHServiceManager 办理,然后被不同模块运用。与模块注册相同,也有 3 种注册办法:

  • 代码动态注册
  • Plist 文件静态注册
  • Annoatation 注册

Service 动态注册

在需求的地方直接利用代码注册 Service ,


[[BeeHive shareInstance]  registerService:@protocol(TradeServiceProtocol) service:[BHTradeViewController class]];

官方的示例是在模块事情的 modInit 中进行注册。

动态注册完结
模块化方案 - BeeHive

终究的完结为:

- (void)registerService:(Protocol *)service implClass:(Class)implClass
{
    // 参数校验
    NSParameterAssert(service != nil);
    NSParameterAssert(implClass != nil);
    if (![implClass conformsToProtocol:service]) {//假设完结类,没有恪守 protocol 则 return 
        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]) {//从 allServicesDict 看 service 是否已注册过,已注册则 return
        if (self.enableException) {//判别反常开关
            @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil];
        }
        return;
    }
    //获取协议名和完结类类名作为 key-value
    NSString *key = NSStringFromProtocol(service);
    NSString *value = NSStringFromClass(implClass);
    if (key.length > 0 && value.length > 0) {//加锁,将协议名和对应类名存进 allServicesDict,
        [self.lock lock];
        [self.allServicesDict addEntriesFromDictionary:@{key:value}];
        [self.lock unlock];
    }
}

首要效果便是协议名和对应类名放到 allServicesDict 保存,等候创立运用。

Serivce 静态注册

经过 BHContext 装备注册 Service 的 Plist 文件:

//Service 静态装备文件
[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";

如图,在 Plist 文件中,装备对应的 ServiceProtocol 与完结类名:

模块化方案 - BeeHive

这就完结 BeeHive 服务注册了。

静态注册完结
模块化方案 - BeeHive

终究的完结为:

- (void)registerLocalServices
{
    //获取设置的文件名
    NSString *serviceConfigName = [BHContext shareInstance].serviceConfigName;
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist"];
    if (!plistPath) {//校验文件是否存在
        return;
    }
    //获取 service 数组
    NSArray *serviceList = [[NSArray alloc] initWithContentsOfFile:plistPath];
    [self.lock lock];
    for (NSDictionary *dict in serviceList) {//遍历 serviceList 数组, 增加协议名与对应的完结类名到 allServicesDict
        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];
}

Serivce Annotation 注册

类似事务模块 Annoatation 注册,Service 注册运用 BeeHiveService() 宏:

@BeeHiveService(HomeServiceProtocol,BHViewController)
@interface BHViewController ()<HomeServiceProtocol>
@end
Annotation 注册完结

打开 BeeHiveService 宏:


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

上述 HomeServiceProtocol 注册示例终究打开为:


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

本质完结,也与模块 Annoatation 注册相同,将数据存储在 segment 中。然后在 App 发动时,经过回调函数 dyld_callback 注册:

模块化方案 - BeeHive

最后会走进 -[BHServcieManager registerService:implClass:] 的办法,和上面服务动态注册的办法相同,增加到 BHServcieManager allServicesDict 字典傍边。

运用 Service

经过 Service 调用其它模块十分简略:

#import "BHService.h"
...
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
...

Service 调用完结

Service 的调用过程如下:

模块化方案 - BeeHive

终究调用 -[BHServiceManager createService:withServiceName:shouldCache:] 的完结:

- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache {
    if (!serviceName.length) {//校验 protocol 名,为空则依据 service 创立
        serviceName = NSStringFromProtocol(service);
    }
    id implInstance = nil;//声明一个为 nil 的完结类变量
    if (![self checkValidService:service]) {//查看 allServicesDict 是否现已由 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) {//假设需求缓存,则先经过 BHContext ,传递 serviceName 查询实例
        id protocolImpl = [[BHContext shareInstance] getServiceInstanceFromServiceName:serviceStr];
        if (protocolImpl) {
            return protocolImpl;
        }
    }
    // 生成 Service 对应的完结类Class 
    Class implClass = [self serviceImplClass:service];
    if ([[implClass class] respondsToSelector:@selector(singleton)]) {//是否完结类 singleton 办法,完结了则调用不同的办法进行初始化
        if ([[implClass class] singleton]) {
            if ([[implClass class] respondsToSelector:@selector(shareInstance)])
                implInstance = [[implClass class] shareInstance];
            else
                implInstance = [[implClass alloc] init];
            if (shouldCache) {//是否需求缓存,假设需求则经过 BHContext 存起来
                [[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
                return implInstance;
            } else {
                return implInstance;
            }
        }
    }
    //implClass 调用初始化办法,直接回来
    return [[implClass alloc] init];
}
- (Class)serviceImplClass:(Protocol *)service
{
    //获取 service 对应的完结类名
    NSString *serviceImpl = [[self servicesDict] objectForKey:NSStringFromProtocol(service)];
    if (serviceImpl.length > 0) {
        //经过完结类名,生成 Class
        return NSClassFromString(serviceImpl);
    }
    return nil;
}

其它

本文重点在 BeeHive 的模块和服务时怎么完结解耦,怎么完结交互协作,关于 BeeHive 的其它细节不会再赘述。

关于怎么运用,官方文档介绍已比较具体,感兴趣可直接去看看 BeeHive 。

总结

作为事务模块化结构,BeeHive 并不是唯一的解决计划,但以 BeeHive 结构为代表的基于面向协议思维的 服务注册 计划,契合开闭原则,让运用者针对接口编程,而不是针对完结编程。且服务注册计划关于代码主动补全和编译时查看都有效,调用简略便利,对事务开发人员来说,作业体感会更好。

BeeHive 劣势在于确实没有做到完全无依靠,客观存在保护公共协议的本钱。但只能说仁者见仁,智者见智。

参考

《手机天猫解耦之路》

《BeeHive,一次iOS模块化解耦实践 – InfoQ》

《打造齐备的iOS组件化计划:怎么面向接口进行模块解耦》

《__attribute__详解及运用》

《Mach-O 文件格局探究》

《BeeHive 1.6.0 源码阅览探讨》

《有赞移动 iOS 组件化(模块化)架构设计实践》