iOS组件化实现

业务背景

APP项目不断迭代,业务不断增加,项目工程越来越复杂。开发团队不断变大,各个人员有自己习惯的代码风格,代码库,第三方等。为了保证业务不断增长扩充,更加便捷管理代码。提出组件化思想。不同模块不同组分别 维护开发。组件化思想核心:解耦合。


模块.png

组件化优势

组件和组件之间没有明确的约束,组件单独开发、单独测试,不能揉入主项目中开发,测试也可以针对性的测试。


路由图.png

实现思路

简单来说就是每个组件可以想象成一个单独的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 

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 

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 

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 

NS_ASSUME_NONNULL_BEGIN

//协议
@protocol AService 
- (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 

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 

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 

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 

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 

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方式,需要写协议,同时参数看起来更加明确。

个人感悟,交流学习,如有不准确请指出!!!

你可能感兴趣的:(iOS组件化实现)