iOS組件化實(shí)現(xiàn)

業(yè)務(wù)背景

APP項(xiàng)目不斷迭代,業(yè)務(wù)不斷增加,項(xiàng)目工程越來(lái)越復(fù)雜。開(kāi)發(fā)團(tuán)隊(duì)不斷變大,各個(gè)人員有自己習(xí)慣的代碼風(fēng)格,代碼庫(kù),第三方等。為了保證業(yè)務(wù)不斷增長(zhǎng)擴(kuò)充,更加便捷管理代碼。提出組件化思想。不同模塊不同組分別 維護(hù)開(kāi)發(fā)。組件化思想核心:解耦合。


模塊.png

組件化優(yōu)勢(shì)

組件和組件之間沒(méi)有明確的約束,組件單獨(dú)開(kāi)發(fā)、單獨(dú)測(cè)試,不能揉入主項(xiàng)目中開(kāi)發(fā),測(cè)試也可以針對(duì)性的測(cè)試。


路由圖.png

實(shí)現(xiàn)思路

簡(jiǎn)單來(lái)說(shuō)就是每個(gè)組件可以想象成一個(gè)單獨(dú)的APP,單獨(dú)的項(xiàng)目。各玩?zhèn)€的。但是不同模塊直接又存在著交互,數(shù)據(jù)交互,頁(yè)面交互,跳轉(zhuǎn)等等。想要跳轉(zhuǎn)又不能相互藕合,就需要一個(gè)“中間商”,中心路由,就是我們要研究的核心。我們主要解決的就是這個(gè)問(wèn)題。

項(xiàng)目管理實(shí)現(xiàn)

不同組件傳入不同Git地址,制作podspec支持pod導(dǎo)入。很簡(jiǎn)單不細(xì)說(shuō),網(wǎng)上很多資料。我們主要來(lái)看看組件間的通信。

實(shí)現(xiàn)方式

網(wǎng)上有很多種實(shí)現(xiàn)方法,總結(jié)大概一下三種:

1.url-block

2.protocol-class

3.target-action

一、url-block

概述:A組件使用URL將Block注冊(cè)給路由,路由以URL為KEY,將Block存儲(chǔ)起來(lái)。B組件通過(guò)URL調(diào)用路由的方法,找到對(duì)應(yīng)Block.完成對(duì)A的調(diào)用。

  //
//  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  參數(shù) @{@"text":...}

#pragma mark -
+ (instancetype)sharedInstance;
// 注冊(cè)
- (void)registerUrl:(NSString *)url block:(RouterBlock)block;
// 執(zhí)行
- (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;
}
// 防止外部調(diào)用alloc 或者 new
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [Router sharedInstance];
}
// 防止外部調(diào)用copy
- (id)copyWithZone:(nullable NSZone *)zone {
    return [Router sharedInstance];
}
// 防止外部調(diào)用mutableCopy
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
    return [Router sharedInstance];
}

// 注冊(cè)
- (void)registerUrl:(NSString *)url block:(RouterBlock)block{
    if (!url || !block) return;
    self.map[url] = block;
}
// 執(zhí)行
- (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];
    
    // 注冊(cè)
    [[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組件調(diào)用A組件
    [[Router sharedInstance] excuteBlockWithUrl:@"A:aTestAction" params:@{@"text":@"文字"}];
}
@end

注冊(cè)所產(chǎn)生的內(nèi)存占用并不是負(fù)擔(dān),主要是大量的注冊(cè)可能會(huì)明顯拖慢啟動(dòng)速度。缺點(diǎn)顯而易見(jiàn): 調(diào)用的時(shí)候傳參不明確

 // B組件調(diào)用A組件
    [[Router sharedInstance] excuteBlockWithUrl:@"A:aTestAction" params:@{@"text":@"文字"}];

2.protocol-class

同樣也是需要存儲(chǔ),根據(jù)協(xié)議存儲(chǔ)類名,注冊(cè)

//
//  Router1.h
//  XXOCDemo
//
//  Created by admin on 2021/12/22.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//協(xié)議
@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;
}
// 防止外部調(diào)用alloc 或者 new
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [Router1 sharedInstance];
}
// 防止外部調(diào)用copy
- (id)copyWithZone:(nullable NSZone *)zone {
    return [Router1 sharedInstance];
}
// 防止外部調(diào)用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];
    // 注冊(cè)
    [[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

通過(guò)performSelector 方法調(diào)用,無(wú)需注冊(cè)

//
//  Router2.h
//  XXOCDemo
//
//  Created by admin on 2021/12/22.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Router2 : NSObject

+ (instancetype)sharedInstance;

/// 調(diào)用方法
/// @param target 類
/// @param action 方法
/// @param params 參數(shù)
- (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

總結(jié)

以上就是所有方法實(shí)現(xiàn),可根據(jù)個(gè)人喜好選擇。用的最多的是URL-Block方式,簡(jiǎn)單注冊(cè)就好,不用寫(xiě)協(xié)議方法,方法調(diào)用統(tǒng)一入口,不會(huì)像protocol,需要寫(xiě)協(xié)議,調(diào)用方法散落各處。實(shí)際上也沒(méi)有太大差別。個(gè)人推薦protocol方式,需要寫(xiě)協(xié)議,同時(shí)參數(shù)看起來(lái)更加明確。

個(gè)人感悟,交流學(xué)習(xí),如有不準(zhǔn)確請(qǐng)指出!!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 最近研究了一下項(xiàng)目的組件化,把casa、bang、limboy的有關(guān)組件化的博客看了一遍,學(xué)到了不少東西,對(duì)目前業(yè)...
    宇立閱讀 1,140評(píng)論 0 3
  • 前言 看了一些關(guān)于組件化文章,決定寫(xiě)篇文章稍稍做些總結(jié)。 一、組件化的誤解 首先筆者認(rèn)為組件化這個(gè)詞用的不合適,應(yīng)...
    ZhengYaWei閱讀 4,713評(píng)論 3 18
  • 背景 隨著公司業(yè)務(wù)的不斷發(fā)展,項(xiàng)目的功能越來(lái)越復(fù)雜,各個(gè)業(yè)務(wù)代碼耦合也越來(lái)越多,代碼量也是急劇增加,傳統(tǒng)的`MVC...
    China架構(gòu)師閱讀 848評(píng)論 0 4
  • 最近研究了一下項(xiàng)目的組件化,把casa、bang、limboy的有關(guān)組件化的博客看了一遍,學(xué)到了不少東西,對(duì)目前業(yè)...
    飛魚(yú)灣閱讀 8,858評(píng)論 2 45
  • 一、蘑菇街 App 的組件化之路 二、iOS應(yīng)用架構(gòu)談 組件化方案 三、蘑菇街 App 的組件化之路·續(xù) 四、iO...
    錢(qián)噓噓閱讀 847評(píng)論 0 1