关于iOS组件化网上资料太多,这里只是从个人观点说明一下怎么使用组件化和使用组件化的优点和缺点
首先下载CTMediatorDemo
Demo的目录结构
项目在没有使用CTMediator之前模块间的关系是这样的
当ModuleA要调用ModuleB和ModuleC的时候,需要#import ModuleB,ModuleC
同样ModuleB要调用ModuleA和ModuleC的时候,需要#import ModuleA,ModuleC 这样耦合程度非常严重
使用了CTMediator之后
ModuleA,ModuleB,ModuleC之间就没有耦合了。
具体实现
CTMediator+ModuleA
#import "CTMediator.h"
NS_ASSUME_NONNULL_BEGIN
@interface CTMediator (ModuleA)
- (BaseViewController *)mediator_ModuleAPage1ViewController:(NSMutableDictionary *)params;
- (BaseViewController *)mediator_ModuleAPage2ViewController:(NSMutableDictionary *)params completion:(void (^)(NSDictionary *))completion;
@end
NS_ASSUME_NONNULL_END
#import "CTMediator+ModuleA.h"
// 字符串 是类名 Target_xxx.h 中的 xxx 部分
NSString * const kCTMediatorTarget_ModuleA = @"ModuleA";
// 字符串是 Target_xxx.h 中 定义的 Action_xxxx 函数名的 xxx 部分
NSString * const kCTMediatorActionNativTo_ModuleAPage1ViewController = @"ModuleAPage1ViewController";
NSString * const kCTMediatorActionNativTo_ModuleAPage2ViewController = @"ModuleAPage2ViewController";
@implementation CTMediator (ModuleA)
- (BaseViewController *)mediator_ModuleAPage1ViewController:(NSMutableDictionary *)params{
BaseViewController *viewController = [self performTarget:kCTMediatorTarget_ModuleA
action:kCTMediatorActionNativTo_ModuleAPage1ViewController
params:params shouldCacheTarget:NO];
if ([viewController isKindOfClass:[UIViewController class]]) {
// view controller 交付出去之后,可以由外界选择是push还是present
return viewController;
} else {
// 这里处理异常场景,具体如何处理取决于产品
NSLog(@"%@ 未能实例化页面", NSStringFromSelector(_cmd));
return [[BaseViewController alloc] init];
}
}
- (BaseViewController *)mediator_ModuleAPage2ViewController:(NSMutableDictionary *)params completion:(void (^)(NSDictionary *))completion{
[params setValue:completion forKey:@"callback"];
BaseViewController *viewController = [self performTarget:kCTMediatorTarget_ModuleA
action:kCTMediatorActionNativTo_ModuleAPage2ViewController
params:params shouldCacheTarget:NO];
if ([viewController isKindOfClass:[UIViewController class]]) {
// view controller 交付出去之后,可以由外界选择是push还是present
return viewController;
} else {
// 这里处理异常场景,具体如何处理取决于产品
NSLog(@"%@ 未能实例化页面", NSStringFromSelector(_cmd));
return [[BaseViewController alloc] init];
}
}
@end
Target_ModuleA
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Target_ModuleA : NSObject
- (BaseViewController *)Action_ModuleAPage1ViewController:(NSDictionary *)params;
- (BaseViewController *)Action_ModuleAPage2ViewController:(NSDictionary *)params;
@end
NS_ASSUME_NONNULL_END
#import "Target_ModuleA.h"
#import "ModuleAPage1ViewController.h"
#import "ModuleAPage2ViewController.h"
typedef void (^CallbackType)(NSDictionary *);
@implementation Target_ModuleA
- (BaseViewController *)Action_ModuleAPage1ViewController:(NSDictionary *)params{
ModuleAPage1ViewController *vc = [[ModuleAPage1ViewController alloc]init];
vc.parameter = params;
return vc;
}
- (BaseViewController *)Action_ModuleAPage2ViewController:(NSDictionary *)params{
CallbackType callback = params[@"callback"];
if (callback) {
callback(@{@"status":@"success"});
}
ModuleAPage2ViewController *vc = [[ModuleAPage2ViewController alloc]init];
vc.parameter = params;
return vc;
}
@end
ModuleAPage1ViewController
#import "ModuleAPage1ViewController.h"
@interface ModuleAPage1ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property(strong, nonatomic) UITableView *tableView;
@property(strong, nonatomic) NSArray *dataArray;
@end
@implementation ModuleAPage1ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self setupTableView];
self.dataArray = @[@"ModuleAPage2",@"ModuleBPage1",@"ModuleCPage1"];
[self.tableView reloadData];
}
- (void)setupTableView{
self.tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndetifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndetifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndetifier];
}
cell.textLabel.text = self.dataArray[indexPath.row];
return cell;;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 44.f;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
switch (indexPath.row) {
case 0:
[self pushModuleAPage2];
break;
case 1:
[self pushModuleBPage1];
break;
case 2:
[self presentModuleCPage1];
break;
default:
break;
}
}
- (void)pushModuleAPage2
{
NSMutableDictionary *param = [NSMutableDictionary dictionary];
[param setValue:@"ModuleAPage1" forKey:@"page"];
UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleAPage2ViewController:param completion:^(NSDictionary * _Nonnull dic) {
NSLog(@"%@",dic);
}];
[viewController setHidesBottomBarWhenPushed:YES];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void)pushModuleBPage1
{
UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleBPage1ViewController];
[viewController setHidesBottomBarWhenPushed:YES];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void)presentModuleCPage1
{
UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleCPage1ViewController];
[self.navigationController presentViewController:viewController animated:NO completion:nil];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
只粘贴一部分代码,其余可以看Demo,
从ModuleAPage1ViewController中代码可看出跳转部分都没有#import Module
这就是我们说的说的组件化。传统方式是我们需要跳转那个页面,就需要#import相关的页面,但是使用了CTMediator之后我们只要知道这个页面是属于那个Module,或者说属于那个组件,然后直接调用相关的组件即可。
组件化的优点
举个例子,公司某个App有一个登录???,过一段时间需要研发一个新的App,为了节省时间就用之前App有的登录???,就需要把登录??槌槿〕隼醋龀勺榧?,可能某些同学就会问,这不是私有化Pod库就能实现吗?
接着说即使把登录??樗接谢疨od后,那么假如在B项目中ModuleA,ModuleB,ModuleC都需要验证,如果没有登录就调用登录模块,哪又回到了之前耦合的问题上了ModuleA,ModuleB,ModuleC都需要#import 登录???,所以私有化只是组件的一部分。只有实现组件化才能解耦。
怎么判断项目需要组件化
1.需要组件化首先就需要模块化,就是对业务的高度抽象。需要把相关的业务都抽取到一个??槔锩?。这就不适合创业公司或者只有一个开发人员的公司。小公司和创业公司基本都是在试错,业务基本不稳定。高度抽象业务很难。如果在创业公司项目因业务不稳定,也不建议使用组件化。
2.公司如果只有一个开发人员也不建议使用组件化,组件化最大难度就是高度抽取业务,抽取出来组件化也需要维护,像我们公司一个人开发维护2个App哪就不要给自己找罪受了。
3.如果公司有2个以上开发人员并且时间相对充裕的情况下,在公司业务相对稳定,的情况下可以考虑使用组件化。
4.公司有2个及以上App,有重合的业务可以优先考虑先把重合业务抽取成组件。