- 一种组件化框架
- 将某个模块对外方法使用CBus包装,那么就形成了一个CBus组件
- CBus对外暴露组件名称以及各种组件方法
- CBus封装了组件之间的底层数据通信逻辑,组件与组件之前没有接口依赖
- 更多其他特性
- 组件注册表
- main函数执行之前,所有组件自动注册到一个Map,形成组件注册表
- 后续组件调用,统一来注册表查找组件
- 组件方法转发
- 注册表中通过组件名称找到组件对象
- 在组件对象中查找对应的组件方法并调用之
- 框架层相当于模块与模块之间的胶水层,解除了模块接口依赖
- 组件方法调度
- 允许调用方同步/异步方式调用其他组件方法
- 框架内部维护了一个NSOperationQueue,用于执行异步调用逻辑
- 不用关心其他组件方法是同步或者异步实现
- 生命周期管理
- 在AppDelegate中进行生命周期方法绑定
- 拦截器
- 允许开发者自定义拦截器,实现AOP编程
CBus消除了每个组件之间的依赖关系,可彻底解耦业务依赖。
快速封装一个业务组件:
#import "HomeComponent.h"
#import <CBus/CBusComponent.h>
#import <CBus/CBus.h>
// 实现CBusComponent协议
@interface HomeComponent ()<CBusComponent>
@end
@implementation HomeComponent
// 注册组件,设置组件名称为:home
CBUS_COMPONENT(home)
// 标记一个方法
CBUS_ACTION(testAction) {
CBusResponse *res = [CBusResponse success:@{@"status": @"success", @"message": @"this is a message from test action"}];
// 返回调用结果
[cbus finished:res];
}
以上示例通过 CBUS_COMPONENT
注册了一个 home
组件,同时用 CBUS_ACTION
标记了一个可供外部调用的组件方法。
同步调用:
#import <CBus/CBus.h>
- (void)syncAction {
CBus *cbus = [CBus callRequestWithComponent:@"home" action:@"testAction" params:@{@"name": @"cbus"}];
if (cbus.response.success) {
NSLog(@"CBus response: %@", cbus.response);
}
}
异步调用:
#import <CBus/CBus.h>
- (void)asyncAction {
[CBus asyncCallRequestWithComponent:@"home" action:@"asyncAction" params:nil complete:^(CBus * _Nonnull cbus) {
if (cbus.response.success) {
NSLog(@"CBus response: %@", cbus.response);
}
}];
}
@property (nonatomic, strong, readonly) CBusClient *client;
CBus内部维护一个默认的 CBusClient 对象,如无设置,则返回该对象。
+ (void)setCBusClient:(CBusClient *)client;
设置 CBusClient 对象。一般情况下,不需要设置,因为内部有默认的 CBusClient 对象。对于一些非常重要的业务或者组件,可以单独设置 CBusClient 对象, 保证这些组件或者业务在发起组件调用的时候,能够使用单独的操作队列。
@property (nonatomic, strong, readonly) CBusRequest *request;
每次发起一个调用请求,都会新建 CBusRequest 对象,同时返回一个新的 CBus 对象。request
即是当前调用的请求实例。
CBus 提供了一系列创建 CBusRequest 的快捷方法:
- 同步调用
+ (CBus *)callRequestWithComponent:action:
+ (CBus *)callRequestWithComponent:action:params:
+ (CBus *)callRequestWithComponent:action:params:timeout:
- 异步调用
+ (void)asyncCallRequestWithComponent:action:
+ (void)asyncCallRequestWithComponent:action:params:
+ (void)asyncCallRequestWithComponent:action:params:complete:
+ (void)asyncCallRequestWithComponent:action:params:timeout:complete:
+ (void)asyncCallRequestWithComponent:action:params:completeOnMainThread:
+ (void)asyncCallRequestWithComponent:action:params:timeout:completeOnMainThread:
这些方法,实际内部都会生成一个 CBusRequest 对象,其实质是调用了 CBus 的另外两个方法:
+ (CBus *)execute:(CBusRequest *)request;
+ (void)enqueue:(CBusRequest *)request complete:(CBusAsyncCallCompletion)complete;
@property (nonatomic, strong, readonly) CBusResponse *response;
当前调用的响应结果。
- (void)finished:(CBusResponse *)response;
设置调用结果。前面看到,每个 CBUS_ACTION
标识的组件方法,需要给当前组件调用返回调用结果,那么通过该方法进行设置。
假设组件方法为同步实现,那么在组件方法里面返回结果:
CBUS_ACTION(syncAction) {
CBusResponse *res = [CBusResponse success:@{@"status": @"success", @"message": @"this is a message from test action"}];
// 返回调用结果
[cbus finished:res];
}
假设组件方法为异步实现,那么在异步回调里面返回结果:
CBUS_ACTION(asyncAction) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:3];
CBusResponse *res = [CBusResponse success:@{@"status": @"success"}];
// 返回调用结果
[cbus finished:res];
});
}
不论组件实现是同步或者异步,都需要通过
finised:
来返回调用结果。
当前调用是否已经结束。
当前调用是否已经取消。
- (void)cancel;
取消当前组件调用。
组件与App生命周期方法绑定处理。
+ (BOOL)callAppDelegateActionForSelector:(SEL)aSelector arguments:(NSArray *)arguments;
参考自 Bifrost生命周期处理 。
动态组件注册相关。
+ (void)registerDynamicComponentForClass:(Class)componentClass;
根据类型注册一个动态组件。
+ (void)registerDynamicComponentForName:(NSString *)componentName cls:(Class)componentClass;
给定名称和类型注册一个动态组件。
+ (void)unregisterDynamicComponentForClass:(Class)componentClass;
根据类型取消注册一个动态组件。
+ (void)unregisterDynamicComponentForName:(NSString *)componentName;
根据名称取消注册一个动态组件。
CBusComponent 是组件化协议,每个需要进行组件化的模块都需要实现该协议。
CBUS_COMPONENT(name)
使用该宏进行静态组件的注册,其定义为:
#define CBUS_COMPONENT(name) \
CBUS_EXTERN void CBusResigterComponent(Class); \
+ (NSString *)componentName { return @#name; } \
+ (void)load { CBusResigterComponent(self); }
因此 App 会在执行 main
函数之前,进行组件的注册。对于静态组件,注册的时候会新建一个空实例,并添加至注册表。如果业务方提供了 name
,那么将作为组件
的名称添加到注册表中,用于后续的组件调用。如果没有提供 name
,组件类型将作为其注册表的 key
。
CBUS_DYNAMIC_COMPONENT(name)
使用该宏进行动态态组件的注册。相对于静态组件,动态组件的注册只会注册其类型,不会实例化空对象,后续业务方可在必要时机进行动态组件的注册。该宏定义:
#define CBUS_DYNAMIC_COMPONENT(name) \
CBUS_COMPONENT(#name) \
+ (BOOL)isDynamic { return YES; } \
CBUS_ACTION(action)
CBus 为了能够正确识别匹配组件的目标方法,定义了该宏:
#define CBUS_ACTION(sel_name) \
- (void)__cbus_action__##sel_name:(CBus *)cbus CBUS_OBJC_DYNAMIC
可以看到,该宏为每个标记的组件方法,添加了 __cbus_action__
的前缀,并传入了一个当前调用的 CBus 对象,业务方可通过访问 cbus
实例来
获取调用请求,或是设置返回调用结果。
+ (BOOL)isDynamic;
是否动态组件。
+ (NSString *)componentName;
当前组件名称。
CBus 内核中持有一个默认的 CBusClient 对象,该对象是方法调用的实际管理者,其主要包含以下功能:
- 初始化调用请求
- 持有方法线程调度管理者
- 设置全局的拦截器
@property (nonatomic, strong, readonly) CBusDispatcher *dispatcher;
方法线程调度管理者,具体见 CBusDispatcher 。
@property (nonatomic, copy, readonly) NSArray<id<CBusInterceptor>> *interceptors;
自定义拦截器列表,全局生效。
- (CBusRealCall *)newCall:(CBus *)cbus;
初始化一个调用请求。
- (void)addInterceptor:(id<CBusInterceptor>)interceptor;
添加一个拦截器。
- (void)addInterceptors:(NSArray<id<CBusInterceptor>> *)interceptors;
批量添加拦截器。
每次 CBus 的方法调用,都需要生成一个 CBusRequest 实例,该实例中包含了当前请求所有的信息。
@property (nonatomic, copy, readonly) NSString *component;
目标组件名称。
@property (nonatomic, copy, readonly) NSString *action;
目标组件方法名称。
@property (nonatomic, strong, readonly) NSDictionary *params;
参数实体。
@property (nonatomic, assign, readonly) NSTimeInterval timeout;
当前请求的超时时间。
@property (nonatomic, assign, readonly) BOOL isDeliverOnMainThread;
是否在主线程结束回调。
@property (nonatomic, copy, readonly) NSArray<id<CBusInterceptor>> *interceptors;
当前请求的拦截器。
可通过以下两个方法,生成对应的请求实体:
+ requestWithComponent:action:params:
+ requestWithComponent:action:params:timeout
;
- (void)deliverOnMainThread;
结束回调切换回主线程。
可添加单个或多个拦截器:
- (void)addInterceptor:(id<CBusInterceptor>)interceptor;
:添加一个请求拦截器- (void)addInterceptors:(NSArray<id<CBusInterceptor>> *)interceptors;
:批量添加请求拦截器
可直接通过 aRequest.timeout = 4
来手动设置超时时间。
CBusResponse 中包含了每个方法调用的响应结果。
@property (nonatomic, strong, readonly) NSDictionary *result;
响应结果。
@property (nonatomic, assign, readonly) CBusCode code;
响应码。
@property (nonatomic, assign, readonly, getter=isSuccess) BOOL success;
是否响应成功。
根据不同场景,提供了以下方法用于快速生成不同响应结果:
+ (instancetype)success:(NSDictionary *)result;
:快速返回一个成功结果,其code=1, isSuccess=YES
+ (instancetype)errorCode:(CBusCode)code;
:根据给定的code
,快速返回一个失败结果,其isSuccess=NO
+ (instancetype)error:(NSDictionary *)result;
:快速返回一个失败结果,其isSuccess=NO
+ (instancetype)error:(nullable NSDictionary *)result code:(CBusCode)code;
:快速返回一个自定义的失败结果,其isSuccess=NO
CBus 所有的异步调用,都将加入到 CBusDispatcher 中维护的操作队列中,其默认的最大操作数是64。
- (void)enqueue:(CBusAsyncCall *)asyncCall;
开始执行一个异步调用。
- (void)onResult:(CBus *)cbus completion:(CBusAsyncCallCompletion)completion;
触发异步方法回调。
- (void)cancelAllCalls;
取消所有的异步方法调用。
TODO