业务背景
APP项目不断迭代,业务不断增加,项目工程越来越复杂。开发团队不断变大,各个人员有自己习惯的代码风格,代码库,第三方等。为了保证业务不断增长扩充,更加便捷管理代码。提出组件化思想。不同模块不同组分别 维护开发。组件化思想核心:解耦合。
组件化优势
组件和组件之间没有明确的约束,组件单独开发、单独测试,不能揉入主项目中开发,测试也可以针对性的测试。
实现思路
简单来说就是每个组件可以想象成一个单独的APP,单独的项目。各玩个的。但是不同模块直接又存在着交互,数据交互,页面交互,跳转等等。想要跳转又不能相互藕合,就需要一个“中间商”,中心路由,就是我们要研究的核心。我们主要解决的就是这个问题。
项目管理实现
不同组件传入不同Git地址,制作podspec支持pod导入。很简单不细说,网上很多资料。我们主要来看看组件间的通信。
实现方式
网上有很多种实现方法,总结大概一下三种:
1.url-block
2.protocol-class
3.target-action
一、url-block
概述:A组件使用URL将Block注册给路由,路由以URL为KEY,将Block存储起来。B组件通过URL调用路由的方法,找到对应Block.完成对A的调用。
//
// Router.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^RouterBlock)(NSDictionary *dic);
@interface Router : NSObject
#pragma mark - URL
// A:aTestAction 参数 @{@"text":...}
#pragma mark -
+ (instancetype)sharedInstance;
// 注册
- (void)registerUrl:(NSString *)url block:(RouterBlock)block;
// 执行
- (void)excuteBlockWithUrl:(NSString *)key params:(NSDictionary *)params;
@end
NS_ASSUME_NONNULL_END
//
// Router.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "Router.h"
@interface Router()
@property (strong, nonatomic) NSMutableDictionary *map;
@end
@implementation Router
+ (instancetype)sharedInstance {
static Router *_sharedSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [[super allocWithZone:NULL] init];
});
return _sharedSingleton;
}
// 防止外部调用alloc 或者 new
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [Router sharedInstance];
}
// 防止外部调用copy
- (id)copyWithZone:(nullable NSZone *)zone {
return [Router sharedInstance];
}
// 防止外部调用mutableCopy
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
return [Router sharedInstance];
}
// 注册
- (void)registerUrl:(NSString *)url block:(RouterBlock)block{
if (!url || !block) return;
self.map[url] = block;
}
// 执行
- (void)excuteBlockWithUrl:(NSString *)key params:(NSDictionary *)params {
if (!key) return;
RouterBlock block = self.map[key];
if (!block) return;
block(params);
}
@end
//
// AModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AModule : UIViewController
@end
NS_ASSUME_NONNULL_END
//
// AModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "AModule.h"
#import "Router.h"
@interface AModule ()
@end
@implementation AModule
- (void)viewDidLoad {
[super viewDidLoad];
// 注册
[[Router sharedInstance] registerUrl:@"A:aTestAction" block:^(NSDictionary * _Nonnull dic) {
[self aTestAction:dic[@"text"]];
}];
}
- (void)aTestAction:(NSString *)text{
NSLog(text);
}
@end
//
// BModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface BModule : UIViewController
@end
NS_ASSUME_NONNULL_END
// BModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
#import "BModule.h"
#import "Router.h"
@interface BModule ()
@end
@implementation BModule
- (void)viewDidLoad {
[super viewDidLoad];
// B组件调用A组件
[[Router sharedInstance] excuteBlockWithUrl:@"A:aTestAction" params:@{@"text":@"文字"}];
}
@end
注册所产生的内存占用并不是负担,主要是大量的注册可能会明显拖慢启动速度。缺点显而易见: 调用的时候传参不明确
// B组件调用A组件
[[Router sharedInstance] excuteBlockWithUrl:@"A:aTestAction" params:@{@"text":@"文字"}];
2.protocol-class
同样也是需要存储,根据协议存储类名,注册
//
// Router1.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//协议
@protocol AService <NSObject>
- (void)aTestAction:(NSString *)text;
@end
@interface Router1 : NSObject
+ (instancetype)sharedInstance;
- (void)registerService:(Protocol *)service class:(Class)cls;
- (id)getObject:(Protocol *)service;
@end
NS_ASSUME_NONNULL_END
//
// Router1.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "Router1.h"
@interface Router1()
@property (strong, nonatomic) NSMutableDictionary *map;
@end
@implementation Router1
+ (instancetype)sharedInstance {
static Router1 *_sharedSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [[super allocWithZone:NULL] init];
});
return _sharedSingleton;
}
// 防止外部调用alloc 或者 new
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [Router1 sharedInstance];
}
// 防止外部调用copy
- (id)copyWithZone:(nullable NSZone *)zone {
return [Router1 sharedInstance];
}
// 防止外部调用mutableCopy
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
return [Router1 sharedInstance];
}
- (void)registerService:(Protocol *)service class:(Class)cls {
if (!service || !cls) return;
self.map[NSStringFromProtocol(service)] = cls;
}
- (id)getObject:(Protocol *)service {
if (!service) return nil;
Class cls = self.map[NSStringFromProtocol(service)];
id obj = [cls new];
if ([obj conformsToProtocol:service]) {
return obj;
}
return nil;
}
@end
//
// AModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AModule : UIViewController
- (void)aTestAction:(NSString *)text;
@end
NS_ASSUME_NONNULL_END
//
// AModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "AModule.h"
#import "Router1.h"
@interface AModule ()
@end
@implementation AModule
- (void)viewDidLoad {
[super viewDidLoad];
// 注册
[[Router1 sharedInstance] registerService:@protocol(AService) class:self.class];
}
- (void)aTestAction:(NSString *)text{
NSLog(@"%@", text);
}
@end
//
// BModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface BModule : UIViewController
@end
NS_ASSUME_NONNULL_END
//
// BModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "BModule.h"
#import "Router1.h"
#import "AModule.h"
@interface BModule ()
@end
@implementation BModule
- (void)viewDidLoad {
[super viewDidLoad];
AModule *service = [[Router1 sharedInstance] getObject:@protocol(AService)];
[service aTestAction:@"文字"];
}
@end
3.target-action
通过performSelector 方法调用,无需注册
//
// Router2.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Router2 : NSObject
+ (instancetype)sharedInstance;
/// 调用方法
/// @param target 类
/// @param action 方法
/// @param params 参数
- (id)performWithTarget:(NSString *)target action:(NSString *)action params:(NSDictionary *)params;
@end
NS_ASSUME_NONNULL_END
//
// Router2.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "Router2.h"
@implementation Router2
+ (instancetype)sharedInstance {
static Router2 *_sharedSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [[super allocWithZone:NULL] init];
});
return _sharedSingleton;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [Router2 sharedInstance];
}
- (id)copyWithZone:(nullable NSZone *)zone {
return [Router2 sharedInstance];
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
return [Router2 sharedInstance];
}
- (id)performWithTarget:(NSString *)target action:(NSString *)action params:(NSDictionary *)params {
Class cls; id obj; SEL sel;
cls = NSClassFromString(target);
if (!cls){
return nil;
}
sel = NSSelectorFromString(action);
if (!sel){
return nil;
}
obj = [cls new];
if (![obj respondsToSelector:sel]) {
return nil;
}
return [obj performSelector:sel withObject:params];
}
@end
//
// AModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AModule : UIViewController
- (void)aTestAction:(NSString *)text;
@end
NS_ASSUME_NONNULL_END
//
// AModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "AModule.h"
@implementation AModule
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)aTestAction:(NSString *)text{
NSLog(@"%@", text);
}
@end
//
// BModule.h
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface BModule : UIViewController
@end
NS_ASSUME_NONNULL_END
//
// BModule.m
// XXOCDemo
//
// Created by admin on 2021/12/22.
//
#import "BModule.h"
#import "Router2.h"
@interface BModule ()
@end
@implementation BModule
- (void)viewDidLoad {
[super viewDidLoad];
[[Router2 sharedInstance] performWithTarget:@"AModule" action:@"aTestAction:" params:@{@"text":@"文字"}];
}
@end
总结
以上就是所有方法实现,可根据个人喜好选择。用的最多的是URL-Block方式,简单注册就好,不用写协议方法,方法调用统一入口,不会像protocol,需要写协议,调用方法散落各处。实际上也没有太大差别。个人推荐protocol方式,需要写协议,同时参数看起来更加明确。
个人感悟,交流学习,如有不准确请指出!!!