Wrappres' Studio.

组件化之 Protocol 匹配

字数统计: 933阅读时长: 4 min
2020/10/05 Share

组件化之 Protocol 匹配

iOS 中主要有三种组件化方式,URL Route、Target-Action、Protocol 匹配。这里主要讲第一种:Protocol 匹配。

这类方式的大致思想是通过 Protocol 来获取 class,再动态创建实例。

优缺点

优点

  • 利用接口调用,实现了参数传递时的类型安全
  • 直接使用模块的 protocol 接口,无需再重复封装

    缺点

  • 用 OC runtime 创建对象,不支持 Swift

  • 由框架来创建所有对象,创建方式有限,例如不支持外部传入参数,再调用自定义初始化方法
  • 无法保证所使用的 protocol 一定存在对应的模块,也无法直接判断某个 protocol 是否能用于获取模块

BeeHive

实现思路是将 protocol 和对应的类进行字典匹配,之后就可以用 protocol 获取 class,再动态创建实例。

使用

1
2
// 注册模块 (protocol-class 匹配)
[[BeeHive shareInstance] registerService:@protocol(EditorViewProtocol) service:[EditorViewController class]];
1
2
// 获取模块 (用 runtime 创建 EditorViewController 实例)
id<EditorViewProtocol> editor = [[BeeHive shareInstance] createService:@protocol(EditorViewProtocol)];

注册 DynamicModule

  1. BeeHive.plist中配置的moduleservice是在 AppDelegate 中调用[[BeeHive shareInstance] setContext:[BHContext shareInstance]];时加载。
  2. Module 的实现中+load内部调用[BeeHive registerDynamicModule:[self class]];动态加载。
  3. 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
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
- (void)registerService:(Protocol *)service implClass:(Class)implClass
{
NSParameterAssert(service != nil);
NSParameterAssert(implClass != nil);

if (![implClass conformsToProtocol:service]) {
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]) {
if (self.enableException) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil];
}
return;
}

NSString *key = NSStringFromProtocol(service);
NSString *value = NSStringFromClass(implClass);

if (key.length > 0 && value.length > 0) {
[self.lock lock];
[self.allServicesDict addEntriesFromDictionary:@{key:value}];
[self.lock unlock];
}

}

调用 Service

示例:

1
id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

底层实现:

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
- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache {
if (!serviceName.length) {
serviceName = NSStringFromProtocol(service);
}
id implInstance = nil;

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];
}

根据传入的ProtocolservicesDict中寻找相应的类,并动态创建类。

Swinject

相比直接 protocol-class匹配的方式,protocol-block的方式更加易用。同时也不需要依赖runtime,可以完美的支持Swift
示例代码:

1
2
3
4
5
6
7
8
let container = Container()

// 注册模块
container.register(EditorViewProtocol.self) { _ in
return EditorViewController()
}
// 获取模块
let editor = container.resolve(EditorViewProtocol.self)!

CATALOG
  1. 1. 组件化之 Protocol 匹配
    1. 1.1. 优缺点
      1. 1.1.1. 优点
      2. 1.1.2. 缺点
    2. 1.2. BeeHive
      1. 1.2.1. 使用
      2. 1.2.2. 注册 DynamicModule
      3. 1.2.3. 注册 Protocol 关联 Service
      4. 1.2.4. 调用 Service
    3. 1.3. Swinject