前言 本文主要讲组件化之间是如何 通讯
的
组件化通讯方案 目前的主流方式有三种:
URL
路由
target-action
protocol
匹配
URL路由 目前iOS上大部分路由工具都是基于URL匹配的,或者根据命名约定,用runtime方法进行动态调用
这些动态化的方案的有点是实现简单,缺点是需要维护 字符串表
,或者依赖于命名约定,无法在编译时暴露出所有问题,需要在运行时才能发现错误
URL路由方式主要是以蘑菇街为代表的 MGJRouter
其实现思路是:
1 2 3 4 5 6 7 // 1.注册某个URL MGJRouter.registerURLPattern("app://home") { (info) in print("info:\(info)") } // 2.调用路由 MGJRouter.openURL("app://home")
URL路由的优点
极高的动态性,适合经常开展运营活动的App,例如电商
方便地统一管理多平台的路由规则
易于适配URL Scheme
URL路由的缺点
传参方式有限,并且无法利用编译器进行参数类型检查,因此所有的参数都是通过字符串转换而来
只适用于界面模块,不适用于通用模块
参数的格式不明确,是个灵活的 dictionary,也需要有个地方可以查参数格式
不支持storyboard
依赖于字符串硬编码,难以管理,蘑菇街做了个后台专门管理
无法保证所使用的模块一定存在
解耦能力有限,url的注册、实现、使用必须用相同的字符规则,一旦任何一方作出修改都会导致其他方的代码失效,并且重构难度大
除了 MGJRouter
,还有以下这些三方框架
target-action 这个方案是基于OC的 runtime、category
特性动态获取模块,例如通过 NSClassFromString
获取类并创建实例,通过 performSelector + NSInvocation
动态调用方法
其主要的代表框架是 casatwy的CTMediator
其实现思路是:
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 //******* 1、分类定义新接口 extension CTMediator{ @objc func A_showHome()->UIViewController?{ let params = [ kCTMediatorParamsKeySwiftTargetModuleName: "ZJBase_Example" ] if let vc = self.performTarget("A", action: "Extension_HomeViewController", params: params, shouldCacheTarget: false) as? UIViewController{ return vc } return nil } } //******* 2、模块提供者提供target-action的调用方式(对外需要加上public关键字) class Target_A: NSObject { @objc func Action_Extension_HomeViewController(_ params: [String: Any])->UIViewController{ let home = HomeViewController() return home } } //******* 3、使用 if let vc = CTMediator.sharedInstance().A_showHome() { self.navigationController?.pushViewController(vc, animated: true) }
其模块间的引用关系如下图所示:
缺点
需要在 mediator
和 target
中重新添加每一个接口,模块化时代码较为繁琐
在 category
中仍然引入了 字符串编码
,内部使用字典传参,一定程度上也存在和URL路由相同的问题
无法保证使用的模块一定存在,target在修改后,使用者只能在运行时才能发现错误
可能会创建过多的 target
类
CTMediator源码分析
通过分类中调用的 performTarget
来到 CTMediator
中的具体实现,即 performTarget:action:params:shouldCacheTarget:
,主要是通过传入的name,找到对应的 target和action
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 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget { if (targetName == nil || actionName == nil) { return nil; } // 在swift中使用时,需要传入对应项目的target名称,否则会找不到视图控制器 NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName]; // generate target 生成target NSString *targetClassString = nil; if (swiftModuleName.length > 0) { //swift中target文件名拼接 targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName]; } else { //OC中target文件名拼接 targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; } //缓存中查找target NSObject *target = [self safeFetchCachedTarget:targetClassString]; //缓存中没有target if (target == nil) { //通过字符串获取对应的类 Class targetClass = NSClassFromString(targetClassString); //创建实例 target = [[targetClass alloc] init]; } // generate action 生成action方法名称 NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; //通过方法名字符串获取对应的sel SEL action = NSSelectorFromString(actionString); if (target == nil) { // 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的 [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; return nil; } //是否需要缓存 if (shouldCacheTarget) { [self safeSetCachedTarget:target key:targetClassString]; } //是否响应sel if ([target respondsToSelector:action]) { //动态调用方法 return [self safePerformAction:action target:target params:params]; } else { // 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理 SEL action = NSSelectorFromString(@"notFound:"); if ([target respondsToSelector:action]) { return [self safePerformAction:action target:target params:params]; } else { // 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。 [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; @synchronized (self) { [self.cachedTarget removeObjectForKey:targetClassString]; } return nil; } } }
进入 safePerformAction: target: params:
实现,主要是通过 invocation
进行 参数传递+消息转发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params { //获取方法签名 NSMethodSignature* methodSig = [target methodSignatureForSelector:action]; if(methodSig == nil) { return nil; } //获取方法签名中的返回类型,然后根据返回值完成参数传递 const char* retType = [methodSig methodReturnType]; //void类型 if (strcmp(retType, @encode(void)) == 0) { ... } //...省略其他类型的判断 }
protocol class protocol匹配的 实现思路
是:
将 protocol
和对应的 类
进行 字典匹配
通过用 protocol
获取 class
,在 动态创建实例
protocol比较典型的三方矿建就是 阿里的BeeHive ,BeeHive
借鉴了Spring Service、Apache DSO的架构理念,采用AOP+扩展App声明周期API
形式,将 业务功能、基础功能
模块以模块方式以解决大型应用中的复杂问题,并让 模块之间以Service形式调用
,将复杂问题切分,以AOP方式模块化服务
BeeHive核心思想
各个模块间调用从直接调用对应模块,变成调用 Service
的形式,避免了直接依赖
App声明周期的分发,将耦合在AppDelegate中逻辑拆分,买个模块以微应用的形式独立存在
示例如下
1 2 3 4 5 6 7 // 1、注册 [[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]]; // 2、使用 #import "BHService.h" id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
优点
利用接口调用,实现了参数传递时的类型安全
直接使用模块的protocol接口,无序再重复封装
缺点
用框架来创建所有对象,创建方式不同,即不支持外部传入参数
用 OC runtime 创建对象,不支持swift
只做了 protocol 和 class 的匹配,不支持更复杂的创建方式和依赖注入
无法保证所使用的protocol一定存在对应的模块,也无法直接判断某个protocol是否能用于获取模块
除了 BeeHive
,还有 Swinject
BeeHive模块注册 在 BeeHive
主要是通过 BHModuleManager
来管理各个模块的。BHModuleManager
中只会管理已经被注册过的模块。
BeeHive提供了三种不同的调用形式,静态plist,动态注册,annotation
。Module、Service之间没有关联,每个业务模块可以单独实现Module或者Service的功能。
Annotation方式注册 这种方式主要是通过 BeeHiveMod
宏进行 Annotation
标记
1 2 3 4 5 6 7 8 9 10 11 12 //***** 使用 BeeHiveMod(ShopModule) //***** BeeHiveMod的宏定义 #define BeeHiveMod(name) \ class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name""; //***** BeeHiveDATA的宏定义 #define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" "))) //***** 全部转换出来后为下面的格式 char * kShopModule_mod __attribute((used, section("__DATA,""BeehiveMods"" "))) = """ShopModule""";
这里针对 __attribute
需要说明以下几点
第一个参数 used
:用来修饰函数,被used修饰以后,意味着即使函数没有被引用,在Release下也不会被优化。如果不加这个修饰,那么Release环境链接器下会去掉没有被引用的段。
通过使用 __attribute__((section("name")))
来指明哪个段。数据则用__attribute__((used))
来标记,防止链接器会优化删除未被使用的段,然后将模块注入到 __DATA
中
进入 BHReadConfiguration
方法,主要是通过 Mach-O
找到存储的数据段,取出放入数组中
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 NSArray<NSString *>* BHReadConfiguration(char *sectionName,const struct mach_header *mhp) { NSMutableArray *configs = [NSMutableArray array]; unsigned long size = 0; #ifndef __LP64__ // 找到之前存储的数据段(Module找BeehiveMods段 和 Service找BeehiveServices段)的一片内存 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; }
读取本地Plist文件
1 [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive"; // 可选,默认为BeeHive.bundle/BeeHive.plist
创建plist文件,Plist文件的格式也是数组中包含多个字典。字典里面有两个Key,一个是@"moduleLevel"
,另一个是 @"moduleClass"
。注意根的数组的名字叫@“moduleClasses”
。
进入 loadLocalModules
方法,主要是从 Plist
里面取出数组,然后把数组加入到BHModuleInfos
数组里面
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 //初始化context时,加载Modules和Services -(void)setContext:(BHContext *)context { _context = context; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self loadStaticServices]; [self loadStaticModules]; }); } 👇 //加载modules - (void)loadStaticModules { // 读取本地plist文件里面的Module,并注册到BHModuleManager的BHModuleInfos数组中 [[BHModuleManager sharedManager] loadLocalModules]; //注册所有modules,在内部根据优先级进行排序 [[BHModuleManager sharedManager] registedAllModules]; } 👇 - (void)loadLocalModules { //plist文件路径 NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"]; //判断文件是否存在 if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) { return; } //读取整个文件[@"moduleClasses" : 数组] NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; //通过moduleClasses key读取 数组 [[@"moduleClass":"aaa", @"moduleLevel": @"bbb"], [...]] 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]]) { //存储到 BHModuleInfos 中 [self.BHModuleInfos addObject:obj]; } }]; }
load方法注册
该方法 注册Module
就是在 Load
方法里面注册Module的类
1 2 3 4 + (void)load { [BeeHive registerDynamicModule:[self class]]; }
进入 registerDynamicModule
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + (void)registerDynamicModule:(Class)moduleClass { [[BHModuleManager sharedManager] registerDynamicModule:moduleClass]; } 👇 - (void)registerDynamicModule:(Class)moduleClass { [self registerDynamicModule:moduleClass shouldTriggerInitEvent:NO]; } 👇 - (void)registerDynamicModule:(Class)moduleClass shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent { [self addModuleFromObject:moduleClass shouldTriggerInitEvent:shouldTriggerInitEvent]; }
其底层还是同第一种方式一样,最终会走到addModuleFromObject:shouldTriggerInitEvent:
方法中
load方法,还可以使用 BH_EXPORT_MODULE
宏代替
1 2 3 #define BH_EXPORT_MODULE(isAsync) \ + (void)load { [BeeHive registerDynamicModule:[self class]]; } \ -(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];}
BH_EXPORT_MODULE
宏里面可以传入一个参数,代表 是否异步加载Module模块
,如果是YES
就是 异步加载
,如果是 NO
就是 同步加载
。
BeeHive 模块事件
BeeHive会给每个模块提供生命周期事件,用于与BeeHive宿主环境进行必要信息交互,感知模块生命周期的变化。
BeeHive各个模块会收到一些事件。在 BHModuleManager
中,所有的事件被定义成了BHModuleEventType
枚举。如下所示,其中有2个事件很特殊,一个是 BHMInitEvent
,一个是 BHMTearDownEvent
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 typedef NS_ENUM(NSInteger, BHModuleEventType) { //设置Module模块 BHMSetupEvent = 0, //用于初始化Module模块,例如环境判断,根据不同环境进行不同初始化 BHMInitEvent, //用于拆除Module模块 BHMTearDownEvent, BHMSplashEvent, BHMQuickActionEvent, BHMWillResignActiveEvent, BHMDidEnterBackgroundEvent, BHMWillEnterForegroundEvent, BHMDidBecomeActiveEvent, BHMWillTerminateEvent, BHMUnmountEvent, BHMOpenURLEvent, BHMDidReceiveMemoryWarningEvent, BHMDidFailToRegisterForRemoteNotificationsEvent, BHMDidRegisterForRemoteNotificationsEvent, BHMDidReceiveRemoteNotificationEvent, BHMDidReceiveLocalNotificationEvent, BHMWillPresentNotificationEvent, BHMDidReceiveNotificationResponseEvent, BHMWillContinueUserActivityEvent, BHMContinueUserActivityEvent, BHMDidFailToContinueUserActivityEvent, BHMDidUpdateUserActivityEvent, BHMHandleWatchKitExtensionRequestEvent, BHMDidCustomEvent = 1000 };
主要分为三种
系统事件
:主要是指 Application生命周期事件!
一般的做法是AppDelegate
改为 继承自BHAppDelegate
1 @interface TestAppDelegate : BHAppDelegate <UIApplicationDelegate>
应用事件
:官方给出的流程图,其中 modSetup、modInit
等,可以用于编码实现各插件模块的设置与初始化。
以上所有的事件都可以通过调用 BHModuleManager
的 triggerEvent:
来处理。
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 - (void)triggerEvent:(NSInteger)eventType { [self triggerEvent:eventType withCustomParam:nil]; } 👇 - (void)triggerEvent:(NSInteger)eventType withCustomParam:(NSDictionary *)customParam { [self handleModuleEvent:eventType forTarget:nil withCustomParam:customParam]; } 👇 #pragma mark - module protocol - (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; //其他3类事件 default: { NSString *selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)]; [self handleModuleEvent:eventType forTarget:nil withSeletorStr:selectorStr andCustomParam:customParam]; } break; } }
从上面的代码中可以发现,除去 BHMInitEvent
初始化事件和 BHMTearDownEvent
拆除Module事件这两个特殊事件以外,所有的事件都是调用的handleModuleEvent:forTarget:withSeletorStr:andCustomParam:
方法,其内部实现主要是遍历 moduleInstances
实例数组,调用 performSelector:withObject:
方法实现对应方法调用
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 - (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 实例数组,调用performSelector:withObject:方法实现对应方法调用 [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)]]; } }]; }
注意:这里所有的 Module
必须是遵循 BHModuleProtocol
的,否则无法接收到这些事件的消息。
BeeHive模块调用
在BeeHive中是通过 BHServiceManager
来管理各个 Protocol
的。BHServiceManager
中只会管理已经被注册过的 Protocol
。
注册 Protocol
的方式总共有三种,和注册 Module
是一样一一对应的
1 2 3 4 5 6 7 8 9 //****** 1、通过BeeHiveService宏进行Annotation标记 BeeHiveService(HomeServiceProtocol,BHViewController) //****** 2、宏定义 #define BeeHiveService(servicename,impl) \ class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}"; //****** 3、转换后的格式,也是将其存储到特殊的段 char * kHomeServiceProtocol_service __attribute((used, section("__DATA,""BeehiveServices"" "))) = "{ \"""HomeServiceProtocol""\" : \"""BHViewController""\"}";
读取本地plist文件
首先同Module一样,需要先设置好路径
1 [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";
同样也是在 setContext
时注册 services
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 //加载services -(void)loadStaticServices { [BHServiceManager sharedManager].enableException = self.enableException; [[BHServiceManager sharedManager] registerLocalServices]; } 👇 - (void)registerLocalServices { NSString *serviceConfigName = [BHContext shareInstance].serviceConfigName; //获取plist文件路径 NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist"]; if (!plistPath) { return; } NSArray *serviceList = [[NSArray alloc] initWithContentsOfFile:plistPath]; [self.lock lock]; //遍历并存储到allServicesDict中 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]; }
load方法注册
在 Load
方法里面注册 Protocol
协议,主要是调用 BeeHive
里面的registerService:service:
完成 protocol
的注册
1 2 3 4 5 6 7 8 9 + (void)load { [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]]; } 👇 - (void)registerService:(Protocol *)proto service:(Class) serviceClass { [[BHServiceManager sharedManager] registerService:proto implClass:serviceClass]; }
到此,三种方式就创建完成了
Protocol的获取
Protocol
与 Module
的区别在于,Protocol
比 Module
多了一个方法,可以返回 Protocol实例对象
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 - (id)createService:(Protocol *)proto; { return [[BHServiceManager sharedManager] createService:proto]; } 👇 - (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; //判断protocol是否已经注册过 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]; }
createService
会先检查Protocol协议是否是注册过的。然后接着取出字典里面对应的Class,如果实现了 shareInstance
方法,那么就创建一个 单例对象
,如果没有,那么就创建一个 实例对象
。如果还实现了singleton,就能进一步的把 implInstance
和serviceStr
对应的加到 BHContext
的 servicesByName
字典里面 缓存
起来。这样就可以随着上下文传递了
进入 serviceImplClass
实现,从这里可以看出 protocol
和 类
是通过 字典
绑定的,protocol
作为key
,serviceImp
(类的名字)作为value
1 2 3 4 5 6 7 8 9 - (Class)serviceImplClass:(Protocol *)service { //通过字典将 协议 和 类 绑定,其中协议作为key,serviceImp(类的名字)作为value NSString *serviceImpl = [[self servicesDict] objectForKey:NSStringFromProtocol(service)]; if (serviceImpl.length > 0) { return NSClassFromString(serviceImpl); } return nil; }
Module & Protocol 这里简单总结下:
辅助类
BHConfig
类:是一个单例,其内部有一个 NSMutableDictionary
类型的 config
属性,该属性维护了一些动态的环境变量,作为 BHContext
的补充存在
BHContext
类:是一个单例,其内部有两个 NSMutableDictionary
的属性,分别是 modulesByName
和 servicesByName
。这个类主要用来保存上下文信息的。例如在 application:didFinishLaunchingWithOptions:
的时候,就可以初始化大量的上下文信息
1 2 3 4 5 //保存信息 [BHContext shareInstance].application = application; [BHContext shareInstance].launchOptions = launchOptions; [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默认为BeeHive.bundle/BeeHive.plist [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";