组件化之 Protocol 匹配
iOS 中主要有三种组件化方式,URL Route、Target-Action、Protocol 匹配。这里主要讲第一种:Protocol 匹配。
这类方式的大致思想是通过 Protocol 来获取 class,再动态创建实例。
优缺点
优点
- 利用接口调用,实现了参数传递时的类型安全
直接使用模块的 protocol 接口,无需再重复封装
缺点
用 OC runtime 创建对象,不支持 Swift
- 由框架来创建所有对象,创建方式有限,例如不支持外部传入参数,再调用自定义初始化方法
- 无法保证所使用的 protocol 一定存在对应的模块,也无法直接判断某个 protocol 是否能用于获取模块
BeeHive
实现思路是将 protocol 和对应的类进行字典匹配,之后就可以用 protocol 获取 class,再动态创建实例。
使用
1 | // 注册模块 (protocol-class 匹配) |
1 | // 获取模块 (用 runtime 创建 EditorViewController 实例) |
注册 DynamicModule
BeeHive.plist
中配置的module
和service
是在AppDelegat
e 中调用[[BeeHive shareInstance] setContext:[BHContext shareInstance]];
时加载。- Module 的实现中
+load
内部调用[BeeHive registerDynamicModule:[self class]];
动态加载。 - Module 的实现中使用注解:
@BeeHiveMod(XXModule)
。
注册的实现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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79- (void)addModuleFromObject:(id)object
shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
Class class;
NSString *moduleName = nil;
if (object) {
class = object;
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)]) {
NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];
BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];
int levelInt = 1;
if (responseBasicLevel) {
levelInt = 0;
}
[moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
if (moduleName) {
[moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
}
[self.BHModuleInfos addObject:moduleInfo];
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) {
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;
}
}];
[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
的类,并根据优先级,进行排序。
注册 Protocol 关联 Service
1 | - (void)registerService:(Protocol *)service implClass:(Class)implClass |
调用 Service
示例:
1 | id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)]; |
底层实现:
1 | - (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache { |
根据传入的Protocol
在servicesDict
中寻找相应的类,并动态创建类。
Swinject
相比直接 protocol-class
匹配的方式,protocol-block
的方式更加易用。同时也不需要依赖runtime
,可以完美的支持Swift
。
示例代码:1
2
3
4
5
6
7
8let container = Container()
// 注册模块
container.register(EditorViewProtocol.self) { _ in
return EditorViewController()
}
// 获取模块
let editor = container.resolve(EditorViewProtocol.self)!