随着项目的发展,不少规模较大的App,例如淘宝、美团之类的App,已经变成了多个App的大集合。有众多的业务线和不一样的开发团队,分别负责开发各自的部分(模块)。相互之间须要调用(通讯)的时候,若是直接引用并调用,就会变成这样。json
能够看出,变如今工程中就是一个工程中,多个模块之间的相互import,耦合在一块儿。bash
1. 全部模块在一个工程里,没法分开编译,开发时每次编译,须要编译所有源码,严重影响开发效率。app
2. 因为全部模块耦合在一块儿,开发A模块功能时,可能须要动到B模块里面的代码。可能会致使B模块的问题。ide
3. 若是淘宝App里面的C模块,想要挪到天猫App里面,几乎确定是要在天猫App中重写一遍C模块的代码了,由于C模块耦合在淘宝App中根本拆不出来。之后C模块的需求开发都要分别在两个App中开发一次。函数
1. 每一个模块分开编译,互不影响。ui
2. 每一个模块分红不一样的仓库,不一样团队进行维护。spa
3. 整个模块能够轻松的复用。3d
首先看下BeeHive给出的结果,demo中的解耦结果是这样:调试
#import "HomeServiceProtocol.h"
id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
// 尽情操做 homeVc,调用 协议HomeServiceProtocol中提供的方法。复制代码
createService:方法,只要传进去一个参数protocol,就能建立出来一个homeVCcode
显然,HomeServiceProtocol和homeVC确定提早偷偷注册进一个字典里了。key是HomeServiceProtocol,value是homeVC。而后给一个protocol就能找到对应的对象。否则不可能根据协议找到实现的。(除非你遍历全部类,扯远了)
homeVC遵照HomeServiceProtocol,因此能够在下面随意调用HomeServiceProtocol中提供的方法,即其余模块须要调用homeVC的全部方法,全都暴露在这个协议中,供其余模块调用。
(这样作就只须要依赖抽象的protocol,而不须要依赖class了)
我在个人模块里调用homeVC,只要protocol中定义了方法,就能调用,并编译经过。进行模块的发版。
从上面的猜想中可知,有一个全局的字典注册了全部protocol与class的对应关系,使后来能够在工程的任意地方都能根据protocol建立出来具体的class,并调用protocol里全部的方法。
看一下注册方式和读取时机。
BeeHiveService的宏定义:
#define BeeHiveService(servicename,impl) \
class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}";复制代码
展开,将参数带进去以后
@class BeeHive;
char * kHomeServiceProtocol_service BeeHiveDATA(BeehiveServices) = "{ ""HomeServiceProtocol"" : ""BHViewController""}";复制代码
BeeHiveDATA的宏定义:
#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))复制代码
彻底展开带进去以后,获得
@class BeeHive;
char * kHomeServiceProtocol_service __attribute((used, section("__DATA,"BeehiveServices" "))) = "{ ""HomeServiceProtocol"" : ""BHViewController""}";复制代码
上面的意思是,会将等号后面的字符串,{ ""HomeServiceProtocol"" : ""BHViewController""}
,存储到mach-o文件的__DATA段的一个名叫BeehiveServices的section中。
在BHAnnotation.m 中(main函数以前)
__attribute__((constructor))
void initProphet() {
_dyld_register_func_for_add_image(dyld_callback);
}复制代码
首先,__attribute__((constructor))修饰的函数,会在main函数以前被调用。
_dyld_register_func_for_add_image函数,dyld加载每一个库的时候,都会调用其中的回调block,即dyld_callback
static void dyld_callback(const struct mach_header *mhp, intptr_t vmaddr_slide)
{
//register services
// 这里是读取以前注册在BeehiveServices的section中的字符串
NSArray<NSString *> *services = BHReadConfiguration("BeehiveServices",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) {
// 这里解析出来 protocol 和对应的class
NSString *protocol = [json allKeys][0];
NSString *clsName = [json allValues][0];
if (protocol && clsName) {
// 注册进全局的BHServiceManager的一个字典里
[[BHServiceManager sharedManager] registerService:NSProtocolFromString(protocol) implClass:NSClassFromString(clsName)];
}
}
}
}
}复制代码
出去出来后转换成键值对,存放在[BHServiceManager shareManager].allServiceDict的字典中。
整个流程图:
放进一个plist文件中来维护。
application:didFinishLauchingWithOptions:(main函数以后)
- (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];
}复制代码
经过上述方式,结构就变成了下图:
ABCD之间相互依赖被解除,若是是模块A的开发者,开发时只须要编译模块A,能够选择性编译模块BCD,不影响运行调试。