iOS 路由 組件化 Target-Action 方案(推薦)

Target-Action方案的優(yōu)點(diǎn)。

  • 充分的利用Runtime的特性,實(shí)現(xiàn)了組件間服務(wù)的自動發(fā)現(xiàn),無需注冊即可實(shí)現(xiàn)組件間的調(diào)用。
  • Target-Action方案,只存在組件依賴Mediator中介的這一層依賴關(guān)系。
  • 在每個組件中創(chuàng)建Mediator的Category分類,針對維護(hù)Mediator的Category分類即可。
  • 每個組件的Category對應(yīng)一個Target類,Categroy中的Action方法對應(yīng)Target類中的Action場景。
  • Target-Action方案統(tǒng)一了所有組件間調(diào)用入口。都是調(diào)用“performTarget: action: params: shouldCacheTarget:”方法。第三個參數(shù)是一個字典,這個字典里面可以傳很多參數(shù),只要Key-Value寫好就可以了。
  • Target-Action方案處理錯誤的方式也統(tǒng)一在一個地方了,Target沒有,或者是Target無法響應(yīng)的Action方法,都可以在Mediator這里進(jìn)行統(tǒng)一出錯處理。
  • Target-Action方案也能有一定的安全保證,它對URL中的Native前綴進(jìn)行安全驗(yàn)證。
  • 因此,Target-Action方案不管從維護(hù)性、可讀性、擴(kuò)展性來說,都是一個比較完美的方案。

Target-Action方案的缺點(diǎn)。

  • Target_Action在Category中將常規(guī)參數(shù)打包成字典,在Target處再把字典拆包成常規(guī)參數(shù),這就造成了一部分的硬編碼。

Target-Action路由方案的技術(shù)實(shí)現(xiàn)。

1、通過Target-Action的方式,創(chuàng)建一個Target類,Target類里面定義一些Action方法,這些方法的結(jié)果是返回一個Controller或其它Object對象。

2、在每個組件中,給Mediator中介類創(chuàng)建一個對應(yīng)這個組件的分類,來保證Mediator中介類的代碼純凈性。

3、在每個組件Mediator的分類中,定義組件外部可調(diào)用的接口方法。接口方法內(nèi)部,通過統(tǒng)一調(diào)用Mediator中介類的“performTarget: action: params: shouldCacheTarget:”方法,實(shí)現(xiàn)組件間解耦。(即調(diào)用者即使不導(dǎo)入其它組件的頭文件,也能調(diào)用其它組件。

4、在Mediator中介類的“performTarget: action: params: shouldCacheTarget:”方法中,主要通過Runtime中的NSClassFromString獲取Target類,通過NSSelectorFromString獲取Target類中的Action方法名。

5、路由方案最終實(shí)現(xiàn)是通過獲取到的Target類和Target類中的Action方法名,去匹配已經(jīng)創(chuàng)建好的Target類,由Target類中的Action方法,真正的實(shí)現(xiàn)創(chuàng)建需要的控制器或?qū)ο?。將這些對象作為方法的返回值,傳遞給調(diào)用者。(即在Target類中的Action方法中,才能真正調(diào)用當(dāng)前組件控制器的功能。)

相關(guān)鏈接

組件化架構(gòu)漫談
http://www.itdecent.cn/p/67a6004f6930

iOS應(yīng)用架構(gòu)談 組件化方案
https://casatwy.com/iOS-Modulization.html

iOS中的三種路由方案實(shí)踐
http://www.itdecent.cn/p/72d705ecc177

iOS 組件化 —— 路由設(shè)計(jì)思路分析
http://www.itdecent.cn/p/76da56b3bd55

Target-Action組件化方案(來自casatwy)

整體架構(gòu)

  • casatwy組件化方案可以處理兩種方式的調(diào)用,遠(yuǎn)程調(diào)用和本地調(diào)用,對于兩個不同的調(diào)用方式分別對應(yīng)兩個接口。
  • 遠(yuǎn)程調(diào)用通過AppDelegate代理方法傳遞到當(dāng)前應(yīng)用后,調(diào)用遠(yuǎn)程接口并在內(nèi)部做一些處理,處理完成后會在遠(yuǎn)程接口內(nèi)部調(diào)用本地接口,以實(shí)現(xiàn)本地調(diào)用為遠(yuǎn)程調(diào)用服務(wù)。
  • 本地調(diào)用由performTarget:action:params:方法負(fù)責(zé),但調(diào)用方一般不直接調(diào)用performTarget:方法。CTMediator會對外提供明確參數(shù)和方法名的方法,在方法內(nèi)部調(diào)用performTarget:方法和參數(shù)的轉(zhuǎn)換。
casatwy提出的組件化架構(gòu)

架構(gòu)設(shè)計(jì)思路

casatwy是通過CTMediator類實(shí)現(xiàn)組件化的,在此類中對外提供明確參數(shù)類型的接口,接口內(nèi)部通過performTarget方法調(diào)用服務(wù)方組件的Target、Action。由于CTMediator類的調(diào)用是通過runtime主動發(fā)現(xiàn)服務(wù)的,所以服務(wù)方對此類是完全解耦的。

但如果CTMediator類對外提供的方法都放在此類中,將會對CTMediator造成極大的負(fù)擔(dān)和代碼量。解決方法就是對每個服務(wù)方組件創(chuàng)建一個CTMediator的Category,并將對服務(wù)方的performTarget調(diào)用放在對應(yīng)的Category中,這些Category都屬于CTMediator中間件,從而實(shí)現(xiàn)了感官上的接口分離。

casatwy組件化實(shí)現(xiàn)細(xì)節(jié)

對于服務(wù)方的組件來說,每個組件都提供一個或多個Target類,在Target類中聲明Action方法。Target類是當(dāng)前組件對外提供的一個“服務(wù)類”,Target將當(dāng)前組件中所有的服務(wù)都定義在里面,CTMediator通過runtime主動發(fā)現(xiàn)服務(wù)。

在Target中的所有Action方法,都只有一個字典參數(shù),所以可以傳遞的參數(shù)很靈活,這也是casatwy提出的去Model化的概念。在Action的方法實(shí)現(xiàn)中,對傳進(jìn)來的字典參數(shù)進(jìn)行解析,再調(diào)用組件內(nèi)部的類和方法。

架構(gòu)分析

casatwy為我們提供了一個Demo,通過這個Demo可以很好的理解casatwy的設(shè)計(jì)思路,下面按照我的理解講解一下這個Demo。

Demo鏈接

https://github.com/casatwy/CTMediator

文件目錄

文件目錄

打開Demo后可以看到文件目錄非常清楚,在上圖中用藍(lán)框框出來的就是中間件部分,紅框框出來的就是業(yè)務(wù)組件部分。我對每個文件夾做了一個簡單的注釋,包含了其在架構(gòu)中的職責(zé)。

遠(yuǎn)程調(diào)用和本地調(diào)用

在CTMediator中定義遠(yuǎn)程調(diào)用和本地調(diào)用的兩個方法,其他業(yè)務(wù)相關(guān)的調(diào)用由Category完成。

// 遠(yuǎn)程App調(diào)用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
// 本地組件調(diào)用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params;

在CTMediator中定義的ModuleA的Category,為其他組件提供了一個獲取控制器并跳轉(zhuǎn)的功能,下面是代碼實(shí)現(xiàn)。由于casatwy的方案中使用performTarget的方式進(jìn)行調(diào)用,所以涉及到很多硬編碼字符串的問題,casatwy采取定義常量字符串來解決這個問題,這樣管理也更方便。

#import "CTMediator+CTMediatorModuleAActions.h"

NSString * const kCTMediatorTargetA = @"A";
NSString * const kCTMediatorActionNativFetchDetailViewController = @"nativeFetchDetailViewController";

@implementation CTMediator (CTMediatorModuleAActions)

- (UIViewController *)CTMediator_viewControllerForDetail {
    UIViewController *viewController = [self performTarget:kCTMediatorTargetA
                                                    action:kCTMediatorActionNativFetchDetailViewController
                                                    params:@{@"key":@"value"}];
    if ([viewController isKindOfClass:[UIViewController class]]) {
        // view controller 交付出去之后,可以由外界選擇是push還是present
        return viewController;
    } else {
        // 這里處理異常場景,具體如何處理取決于產(chǎn)品邏輯
        return [[UIViewController alloc] init];
    }
}

下面是ModuleA組件中提供的服務(wù),被定義在Target_A類中,這些服務(wù)可以被CTMediator通過runtime的方式調(diào)用,這個過程就叫做發(fā)現(xiàn)服務(wù)。

在Target_A中對傳遞的參數(shù)做了處理,以及內(nèi)部的業(yè)務(wù)邏輯實(shí)現(xiàn)。方法是發(fā)生在ModuleA內(nèi)部的,這樣就可以保證組件內(nèi)部的業(yè)務(wù)不受外部影響,對內(nèi)部業(yè)務(wù)沒有侵入性。

- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params {
    // 對傳過來的字典參數(shù)進(jìn)行解析,并調(diào)用ModuleA內(nèi)部的代碼
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    viewController.valueLabel.text = params[@"key"];
    return viewController;
}

命名規(guī)范

在大型項(xiàng)目中代碼量比較大,需要避免命名沖突的問題。

對于這個問題casatwy采取的是加前綴的方式,從casatwy的Demo中也可以看出,其組件ModuleA的Target命名為Target_A,可以區(qū)分各個組件的Target。

被調(diào)用的Action命名為Action_nativeFetchDetailViewController:,可以區(qū)分組件內(nèi)的方法與對外提供的方法。

casatwy將類和方法的命名,都統(tǒng)一按照其功能做區(qū)分當(dāng)做前綴,這樣很好的將組件相關(guān)和組件內(nèi)部代碼進(jìn)行了劃分。


CTMediator 代碼注釋

注釋是自己對代碼的理解,可能會有不對的地方,僅供參考。

調(diào)用路徑

  • 主框架調(diào)用模塊的分類。
  • 模塊的分類調(diào)用統(tǒng)一的中間件CTMediator
  • 中間件CTMediator通過runtime主動發(fā)現(xiàn)服務(wù)。
  • 重點(diǎn)注釋:在這里就實(shí)現(xiàn)了主框架對各個模塊之間的解耦。(主框架不需要導(dǎo)入模塊的頭文件,也能實(shí)現(xiàn)對模塊的調(diào)用)
  • 中間件CTMediator調(diào)用模塊Target中的Action方法。實(shí)現(xiàn)引入模塊的功能。
  • 模塊將創(chuàng)建的控制器等需要實(shí)現(xiàn)的業(yè)務(wù)邏輯返回給主框架。

==主框架==

ViewController

//
//  ViewController.h
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end












//
//  ViewController.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "ViewController.h"
#import <HandyFrame/UIView+LayoutMethods.h>
#import "CTMediator+CTMediatorModuleAActions.h"
#import "TableViewController.h"

// 全局常量
NSString * const kCellIdentifier = @"kCellIdentifier";

@interface ViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSArray *dataSource;

@end

@implementation ViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.view addSubview:self.tableView];
}

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.tableView fill];
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    cell.textLabel.text = self.dataSource[indexPath.row];
    return cell;
}

#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 取消選定由索引路徑標(biāo)識的給定行,并使用使取消選定具有動畫效果的選項(xiàng)。
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    if (indexPath.row == 0) {
        UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];
        
        // 獲得view controller之后,在這種場景下,到底push還是present,其實(shí)是要由使用者決定的,mediator只要給出view controller的實(shí)例就好了
        [self presentViewController:viewController animated:YES completion:nil];
    }
    
    if (indexPath.row == 1) {
        UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];
        [self.navigationController pushViewController:viewController animated:YES];
    }
    
    if (indexPath.row == 2) {
        // 這種場景下,很明顯是需要被present的,所以不必返回實(shí)例,mediator直接present了
        [[CTMediator sharedInstance] CTMediator_presentImage:[UIImage imageNamed:@"image"]];
    }
    
    if (indexPath.row == 3) {
        // 這種場景下,參數(shù)有問題,因此需要在流程中做好處理
        [[CTMediator sharedInstance] CTMediator_presentImage:nil];
    }
    
    if (indexPath.row == 4) {
        [[CTMediator sharedInstance] CTMediator_showAlertWithMessage:@"casa" cancelAction:nil confirmAction:^(NSDictionary *info) {
            // 做你想做的事
        }];
    }
    
    if (indexPath.row == 5) {
        TableViewController *tableViewController = [[TableViewController alloc] init];
        [self presentViewController:tableViewController animated:YES completion:nil];
    }
    
    if (indexPath.row == 6) {
        [[CTMediator sharedInstance] performTarget:@"InvalidTarget" action:@"InvalidAction" params:nil shouldCacheTarget:NO];
    }
}

#pragma mark - getters and setters
- (UITableView *)tableView
{
    if (_tableView == nil) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        // 注冊用于創(chuàng)建新表單元格的類。
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier];
    }
    return _tableView;
}

- (NSArray *)dataSource
{
    if (_dataSource == nil) {
        _dataSource = @[@"present detail view controller",
                        @"push detail view controller",
                        @"present image",
                        @"present image when error",
                        @"show alert",
                        @"table view cell",
                        @"No Target-Action response"
                        ];
    }
    return _dataSource;
}
@end

中間件Mediator

CTMediator+CTMediatorModuleAActions.h

//
//  CTMediator+CTMediatorModuleAActions.h
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "CTMediator.h"

@interface CTMediator (CTMediatorModuleAActions)

/// 中介 視圖詳情控制器
- (UIViewController *)CTMediator_viewControllerForDetail;

/**
 中介 顯示警報信息

 @param message 警報信息
 @param cancelAction 取消動作回調(diào)
 @param confirmAction 確定動作回調(diào)
 */
- (void)CTMediator_showAlertWithMessage:(NSString *)message cancelAction:(void(^)(NSDictionary *info))cancelAction confirmAction:(void(^)(NSDictionary *info))confirmAction;

/// 中介 模態(tài)彈出圖片
- (void)CTMediator_presentImage:(UIImage *)image;

/// 中介 返回cell
- (UITableViewCell *)CTMediator_tableViewCellWithIdentifier:(NSString *)identifier tableView:(UITableView *)tableView;

/// 中介 配置cell
- (void)CTMediator_configTableViewCell:(UITableViewCell *)cell withTitle:(NSString *)title atIndexPath:(NSIndexPath *)indexPath;

/// 中介 清除cell點(diǎn)擊
- (void)CTMediator_cleanTableViewCellTarget;

@end


















//
//  CTMediator+CTMediatorModuleAActions.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "CTMediator+CTMediatorModuleAActions.h"

/// 中介目標(biāo) A
NSString * const kCTMediatorTargetA = @"A";
/// 中介動作 本地獲取詳細(xì)視圖控制器
NSString * const kCTMediatorActionNativeFetchDetailViewController = @"nativeFetchDetailViewController";
/// 中介動作 本地模態(tài)彈出圖片
NSString * const kCTMediatorActionNativePresentImage = @"nativePresentImage";
/// 中介動作 本地沒有圖片
NSString * const kCTMediatorActionNativeNoImage = @"nativeNoImage";
/// 中介動作 展示警報
NSString * const kCTMediatorActionShowAlert = @"showAlert";
/// 中介動作 返回cell
NSString * const kCTMediatorActionCell = @"cell";
/// 中介動作 配置cell
NSString * const kCTMediatorActionConfigCell = @"configCell";

@implementation CTMediator (CTMediatorModuleAActions)

/// 中介 視圖詳情控制器
- (UIViewController *)CTMediator_viewControllerForDetail
{
    UIViewController *viewController = [self performTarget:kCTMediatorTargetA
                                                    action:kCTMediatorActionNativeFetchDetailViewController
                                                    params:@{@"key":@"value"}
                                         shouldCacheTarget:NO
                                        ];
    if ([viewController isKindOfClass:[UIViewController class]]) {
        // view controller 交付出去之后,可以由外界選擇是push還是present
        return viewController;
    } else {
        // 這里處理異常場景,具體如何處理取決于產(chǎn)品
        return [[UIViewController alloc] init];
    }
}

/// 中介 模態(tài)彈出圖片
- (void)CTMediator_presentImage:(UIImage *)image
{
    if (image) {
        [self performTarget:kCTMediatorTargetA
                     action:kCTMediatorActionNativePresentImage
                     params:@{@"image":image}
          shouldCacheTarget:NO];
    } else {
        // 這里處理image為nil的場景,如何處理取決于產(chǎn)品
        [self performTarget:kCTMediatorTargetA
                     action:kCTMediatorActionNativeNoImage
                     params:@{@"image":[UIImage imageNamed:@"noImage"]}
          shouldCacheTarget:NO];
    }
}


/**
 中介 顯示警報信息
 
 @param message 警報信息
 @param cancelAction 取消動作回調(diào)
 @param confirmAction 確定動作回調(diào)
 */
- (void)CTMediator_showAlertWithMessage:(NSString *)message cancelAction:(void(^)(NSDictionary *info))cancelAction confirmAction:(void(^)(NSDictionary *info))confirmAction
{
    // 可變參數(shù)字典,將要發(fā)送出去的參數(shù)
    NSMutableDictionary *paramsToSend = [[NSMutableDictionary alloc] init];
    
    if (message) {
        paramsToSend[@"message"] = message;
    }
    if (cancelAction) {
        paramsToSend[@"cancelAction"] = cancelAction;
    }
    if (confirmAction) {
        paramsToSend[@"confirmAction"] = confirmAction;
    }
    
    [self performTarget:kCTMediatorTargetA
                 action:kCTMediatorActionShowAlert
                 params:paramsToSend
      shouldCacheTarget:NO];
}

/// 中介 返回cell
- (UITableViewCell *)CTMediator_tableViewCellWithIdentifier:(NSString *)identifier tableView:(UITableView *)tableView
{
    return [self performTarget:kCTMediatorTargetA
                        action:kCTMediatorActionCell
                        params:@{
                                 @"identifier":identifier,
                                 @"tableView":tableView
                                 }
             shouldCacheTarget:YES];
}

/// 中介 配置cell
- (void)CTMediator_configTableViewCell:(UITableViewCell *)cell withTitle:(NSString *)title atIndexPath:(NSIndexPath *)indexPath
{
    // perform Target 執(zhí)行目標(biāo)
    [self performTarget:kCTMediatorTargetA
                 action:kCTMediatorActionConfigCell
                 params:@{
                          @"cell":cell,
                          @"title":title,
                          @"indexPath":indexPath
                          }
      shouldCacheTarget:YES];
}

/// 中介 清除cell點(diǎn)擊
- (void)CTMediator_cleanTableViewCellTarget
{
    [self releaseCachedTargetWithTargetName:kCTMediatorTargetA];
}

@end


CTMediator

//
//  CTMediator.h
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <UIKit/UIKit.h>

/// 全局常量 中介參數(shù)關(guān)鍵字 Swift目標(biāo)模塊名稱
extern NSString * const kCTMediatorParamsKeySwiftTargetModuleName;

@interface CTMediator : NSObject

+ (instancetype)sharedInstance;

// 遠(yuǎn)程App調(diào)用入口
/**
 遠(yuǎn)程App調(diào)用入口
 performActionWithUrl 通過遠(yuǎn)程URL執(zhí)行動作

 @param url                 遠(yuǎn)程URL
 @param completion          動作完成后的回調(diào) 字典block參數(shù)
 @return                    返回結(jié)果
 */
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;


// 本地組件調(diào)用入口
/**
 本地組件調(diào)用入口
 performTarget 執(zhí)行目標(biāo)

 @param targetName          目標(biāo)名稱
 @param actionName          動作名稱
 @param params              參數(shù)字典
 @param shouldCacheTarget   是否緩存目標(biāo)
 @return                    返回結(jié)果
 */
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;



/**
 釋放緩存的目標(biāo) 通過目標(biāo)名稱

 @param targetName          目標(biāo)名稱
 */
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName;

@end




















//
//  CTMediator.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "CTMediator.h"
#import <objc/runtime.h>

NSString * const kCTMediatorParamsKeySwiftTargetModuleName = @"kCTMediatorParamsKeySwiftTargetModuleName";

@interface CTMediator ()
/// 緩存目標(biāo)的可變字典
@property (nonatomic, strong) NSMutableDictionary *cachedTarget;

@end

@implementation CTMediator

#pragma mark - public methods
/// 單例
+ (instancetype)sharedInstance
{
    static CTMediator *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[CTMediator alloc] init];
    });
    return mediator;
}

/*
 遠(yuǎn)程App調(diào)用入口
 核心方法
 scheme://[target]/[action]?[params]
 
 url sample:
 aaa://targetA/actionB?id=1234
 */

- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    // 查詢字符串,符合RFC 1808。
    NSString *urlString = [url query];
    // componentsSeparatedByString 返回一個包含已被給定分隔符分隔的來自接收器的子字符串的數(shù)組。
    // 遍歷 被 & 分隔的 urlString數(shù)組
    for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
        // 取出被等號分隔的數(shù)組
        NSArray *elts = [param componentsSeparatedByString:@"="];
        // continue  忽略了當(dāng)次循環(huán)continue語句后的代碼
        if([elts count] < 2) continue;
        [params setObject:[elts lastObject] forKey:[elts firstObject]];
    }
    
    // 這里這么寫主要是出于安全考慮,防止黑客通過遠(yuǎn)程方式調(diào)用本地模塊。這里的做法足以應(yīng)對絕大多數(shù)場景,如果要求更加嚴(yán)苛,也可以做更加復(fù)雜的安全邏輯。
    NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
    if ([actionName hasPrefix:@"native"]) {
        return @(NO);
    }
    
    // 這個demo針對URL的路由處理非常簡單,就只是取對應(yīng)的target名字和method名字,但這已經(jīng)足以應(yīng)對絕大部份需求。如果需要拓展,可以在這個方法調(diào)用之前加入完整的路由邏輯
    id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
    
    // 判斷是否有執(zhí)行完成動作后的回調(diào)字典
    if (completion) {
        // 判斷是否有結(jié)果
        if (result) {
            completion(@{@"result":result});
        } else {
            completion(nil);
        }
    }
    return result;
}

/**
 本地組件調(diào)用入口
 performTarget 執(zhí)行目標(biāo)
 
 @param targetName          目標(biāo)名稱
 @param actionName          動作名稱
 @param params              參數(shù)字典
 @param shouldCacheTarget   是否緩存目標(biāo)
 @return                    返回結(jié)果
 */
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    // Swift模塊名稱
    NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
    
    //==============================================================
    // generate target  生成目標(biāo)
    // 拼接目標(biāo)類名 字符串
    NSString *targetClassString = nil;
    if (swiftModuleName.length > 0) {
        targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
    } else {
        targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    }
    
    // 通過目標(biāo)類名 從緩存字典 取出目標(biāo)
    NSObject *target = self.cachedTarget[targetClassString];
    if (target == nil) {
        // 按名稱獲取類。(將目標(biāo)類的字符串 轉(zhuǎn)化為 目標(biāo)類對象)
        Class targetClass = NSClassFromString(targetClassString);
        // 創(chuàng)建目標(biāo)類的實(shí)例.    alloc:分配內(nèi)存。 init:初始化。
        target = [[targetClass alloc] init];
    }

    //==============================================================
    // generate action  生成動作
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    // NSSelectorFromString 返回帶有給定名稱的選擇器。
    // 通過動作字符串 創(chuàng)建動作方法
    SEL action = NSSelectorFromString(actionString);
    
    //=============================================================
    // 處理 target == nil 的情況.
    // 但是上面已經(jīng)通過 targetClassString = Target_%@ 創(chuàng)建了target對象.
    // 所以 這里可能是多余的.
    if (target == nil) {
        // 這里是處理無響應(yīng)請求的地方之一,這個demo做得比較簡單,如果沒有可以響應(yīng)的target,就直接return了。實(shí)際開發(fā)過程中是可以事先給一個固定的target專門用于在這個時候頂上,然后處理這種請求的
        [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
        return nil;
    }
    
    //================================================================
    // 判斷是否緩存目標(biāo)
    if (shouldCacheTarget) {
        // 將目標(biāo)對象 放入緩存目標(biāo)的可變字典
        self.cachedTarget[targetClassString] = target;
    }

    //================================================================
    // 處理 target 沒有 action 的情況
    // respondsToSelector       返回一個布爾值,該值指示接收方是否實(shí)現(xiàn)或繼承可以響應(yīng)指定消息的方法。
    if ([target respondsToSelector:action]) {
        // 核心方法 安全的執(zhí)行action
        return [self safePerformAction:action target:target params:params];
    } else {
        // 這里是處理無響應(yīng)請求的地方,如果無響應(yīng),則嘗試調(diào)用對應(yīng)target的notFound方法統(tǒng)一處理
        SEL action = NSSelectorFromString(@"notFound:");
        if ([target respondsToSelector:action]) {
            // 核心方法 安全的執(zhí)行action
            return [self safePerformAction:action target:target params:params];
        } else {
            // 這里也是處理無響應(yīng)請求的地方,在notFound都沒有的時候,這個demo是直接return了。實(shí)際開發(fā)過程中,可以用前面提到的固定的target頂上的。
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            [self.cachedTarget removeObjectForKey:targetClassString];
            return nil;
        }
    }
}

/**
 釋放緩存的目標(biāo) 通過目標(biāo)名稱
 
 @param targetName          目標(biāo)名稱
 */
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName
{
    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    [self.cachedTarget removeObjectForKey:targetClassString];
}

#pragma mark - private methods 私有方法
/// 沒有目標(biāo)動作響應(yīng)時 執(zhí)行的方法
- (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams
{
    SEL action = NSSelectorFromString(@"Action_response:");
    NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
    
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    params[@"originParams"] = originParams;
    params[@"targetString"] = targetString;
    params[@"selectorString"] = selectorString;
    
    [self safePerformAction:action target:target params:params];
}



/**
 確保安全執(zhí)行動作的方法 通過OC運(yùn)行時實(shí)現(xiàn)的核心方法

 @param action 執(zhí)行動作的方法
 @param target 目標(biāo)對象(執(zhí)行動作的主體調(diào)用者)
 @param params 參數(shù)字典
 @return 返回模塊通過Action_方法創(chuàng)建出來的對象
 */
- (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
{
    // methodSignatureForSelector 返回一個NSMethodSignature對象,該對象包含由給定選擇器標(biāo)識的方法的描述。
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    // methodReturnType 在Objective-C類型編碼中 對方法的返回類型進(jìn)行編碼的C字符串。
    const char* retType = [methodSig methodReturnType];

    
    
    
    //=============================================================
    /** 注釋
     
     1、iOS 的基礎(chǔ)數(shù)據(jù)類型及其包裝類型:
     1.1、iOS 的基礎(chǔ)數(shù)據(jù)類型
     int、float、double、long、char、NSInteger、NSUInteger、CGFloat、BOOL等
     
     基本數(shù)據(jù)類型——char類型(在計(jì)算機(jī)內(nèi)部以int類型存儲)
     
     在 64 位平臺和類似于 64 位平臺的各種平臺上,
     NSInteger-> long,
     NSUInteger-> unsigned long,
     CGFloat-> double.
     
     1.2、iOS 的基礎(chǔ)數(shù)據(jù)類型與包裝類型的轉(zhuǎn)換
     由于只有對象類型才能放入數(shù)組、字典中,所以需要將基本數(shù)據(jù)類型轉(zhuǎn)換成包裝類型,OC 中提供的包裝類是 NSNumber,NSValue。其中NSNumber 繼承于 NSValue。NSNumber 主要針對于基本數(shù)據(jù)類型的包裝,NSValue 主要針對結(jié)構(gòu)體進(jìn)行包裝。
     
     http://www.itdecent.cn/p/ff2274430b1c
     */
    
    
    /** 注釋
     strcmp函數(shù)
     是string compare(字符串比較)的縮寫,用于比較兩個字符串并根據(jù)比較結(jié)果返回整數(shù)。
     基本形式為strcmp(str1,str2),
     若str1=str2,則返回零;若str1<str2,則返回負(fù)數(shù);若str1>str2,則返回正數(shù)。
     
     @encode(Type)
     @encode()
     作用:用來判斷類型,常和strcmp(ObjCType, @encode(Type))合用。
     @encode(Type) 可以返回該類型的 C 字符串(char *)的表示。
     
     */
    
    
    // 下面這些if語句的作用,應(yīng)該是排除返回值是基本數(shù)據(jù)類型的情況.
    // @encode() 作用:用來判斷類型,常和strcmp(ObjCType, @encode(Type))合用。
    // 判斷 安全執(zhí)行動作方法(即這個方法) 的返回類型 是否為(void)
    if (strcmp(retType, @encode(void)) == 0) {
        // 返回一個能夠使用給定方法簽名構(gòu)造消息的NSInvocation對象。Invocation 調(diào)用
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        // 設(shè)置接收者的參數(shù)。
        /** 注釋
         atIndex 指數(shù)
         指定參數(shù)索引的整數(shù)。
         索引0和1分別表示隱藏參數(shù)self和_cmd;您應(yīng)該使用target和selector屬性直接設(shè)置這些值。對于通常在消息中傳遞的參數(shù),使用索引2或更大。
         */
        [invocation setArgument:&params atIndex:2];
        // 接收器的選擇器,如果沒有設(shè)置則為0。
        [invocation setSelector:action];
        // 接收者的目標(biāo),如果接收者沒有目標(biāo),則為nil。
        [invocation setTarget:target];
        // 將接收方的消息(帶有參數(shù))發(fā)送到其目標(biāo),并設(shè)置返回值。
        [invocation invoke];
        return nil;
    }

    // 判斷 安全執(zhí)行動作方法(即這個方法) 的返回類型 是否為(NSInteger)
    if (strcmp(retType, @encode(NSInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        
        NSInteger result = 0;
        // 獲取接收者的返回值。
        // 將模塊中 方法的返回值得實(shí)際值 賦值給result
        [invocation getReturnValue:&result];
        // 返回oc對象(將返回值的實(shí)際值 包裝成OC對象)
        return @(result);
    }

    // 判斷 安全執(zhí)行動作方法(即這個方法) 的返回類型 是否為(BOOL)
    if (strcmp(retType, @encode(BOOL)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        BOOL result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    // 判斷 安全執(zhí)行動作方法(即這個方法) 的返回類型 是否為(CGFloat)
    if (strcmp(retType, @encode(CGFloat)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        CGFloat result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    // 判斷 安全執(zhí)行動作方法(即這個方法) 的返回類型 是否為(NSUInteger)
    if (strcmp(retType, @encode(NSUInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSUInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    
/** 注釋
 詳情鏈接
 https://www.cnblogs.com/lurenq/p/7709731.html
 首先#pragma在本質(zhì)上是聲明,常用的功能就是注釋,尤其是給Code分段注釋;
 而且它還有另一個強(qiáng)大的功能是處理編譯器警告,但卻沒有上一個功能用的那么多。
 
 clang diagnostic 是#pragma 第一個常用命令:
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-相關(guān)命令"
 // 你自己的代碼
 #pragma clang diagnostic pop
 */
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    // 在這里寫 自己的代碼;
    // 這三行 #pragma clang diagnostic 代碼,是忽略警告的固定格式.
    // 如果你確定不會發(fā)生內(nèi)存泄漏的情況下,可以使用如下的語句來忽略掉這條警告 "-Warc-performSelector-leaks"
    
    // 以對象作為參數(shù) 向接收者發(fā)送消息。
    // 中介的核心代碼,目標(biāo)通過參數(shù)執(zhí)行動作以后,得到Target_A執(zhí)行后返回的對象.
    // 實(shí)際上,真正創(chuàng)建出所需要的實(shí)例對象的類 是Target_A的類.(Target_A是真正干活的)
    id Target_Object = [target performSelector:action withObject:params];
    return Target_Object;
    
#pragma clang diagnostic pop
    
}

#pragma mark - getters and setters
- (NSMutableDictionary *)cachedTarget
{
    if (_cachedTarget == nil) {
        _cachedTarget = [[NSMutableDictionary alloc] init];
    }
    return _cachedTarget;
}

@end

模塊部分

Target_A

//
//  Target_A.h
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface Target_A : NSObject

/// 動作 本地獲取詳情視圖控制器
- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params;
/// 動作 本地模態(tài)彈出圖片
- (id)Action_nativePresentImage:(NSDictionary *)params;
/// 動作 展示警報
- (id)Action_showAlert:(NSDictionary *)params;

// 容錯
- (id)Action_nativeNoImage:(NSDictionary *)params;

@end






















//
//  Target_A.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "Target_A.h"
#import "DemoModuleADetailViewController.h"

typedef void (^CTUrlRouterCallbackBlock)(NSDictionary *info);

@implementation Target_A

- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params
{
    // 因?yàn)閍ction是從屬于ModuleA的,所以action直接可以使用ModuleA里的所有聲明
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    viewController.valueLabel.text = params[@"key"];
    return viewController;
}

- (id)Action_nativePresentImage:(NSDictionary *)params
{
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    viewController.valueLabel.text = @"this is image";
    // 通過參數(shù)字典 取出字典中的對象
    viewController.imageView.image = params[@"image"];
    // 窗口的根控制器 模態(tài)彈出 視圖控制器
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
    // id 的返回值可以為空
    return nil;
}

- (id)Action_showAlert:(NSDictionary *)params
{
    // 創(chuàng)建并返回具有指定標(biāo)題和行為的操作。
    // handler : 處理程序
    // 當(dāng)用戶選擇操作時要執(zhí)行的塊。這個塊沒有返回值,只接受選擇的action對象作為它的唯一參數(shù)。
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        // 重點(diǎn)
        // 通過參數(shù)字典 取出回調(diào)的block;參數(shù)字典中,包含回調(diào)block對象.
        CTUrlRouterCallbackBlock callback = params[@"cancelAction"];
        // 如果回調(diào)有值,執(zhí)行回調(diào)動作.
        if (callback) {
            callback(@{@"alertAction":action});
        }
    }];
    
    UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"confirm" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        CTUrlRouterCallbackBlock callback = params[@"confirmAction"];
        if (callback) {
            callback(@{@"alertAction":action});
        }
    }];
    
    // 創(chuàng)建 警報控制器
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"alert from Module A" message:params[@"message"] preferredStyle:UIAlertControllerStyleAlert];
    // 添加動作
    [alertController addAction:cancelAction];
    [alertController addAction:confirmAction];
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
    return nil;
}

/// 動作 本地沒有圖片(用來容錯處理)
- (id)Action_nativeNoImage:(NSDictionary *)params
{
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    viewController.valueLabel.text = @"no image";
    viewController.imageView.image = [UIImage imageNamed:@"noImage"];
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
    
    return nil;
}

/// 通過動作拿到cell
- (UITableViewCell *)Action_cell:(NSDictionary *)params
{
    UITableView *tableView = params[@"tableView"];
    NSString *identifier = params[@"identifier"];
    
    // 這里的TableViewCell的類型可以是自定義的,我這邊偷懶就不自定義了。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    return cell;
}

- (id)Action_configCell:(NSDictionary *)params
{
    NSString *title = params[@"title"];
    NSIndexPath *indexPath = params[@"indexPath"];
    UITableViewCell *cell = params[@"cell"];
    
    // 這里的TableViewCell的類型可以是自定義的,我這邊偷懶就不自定義了。
    cell.textLabel.text = [NSString stringWithFormat:@"%@,%ld", title, (long)indexPath.row];
    
//    if ([cell isKindOfClass:[XXXXXCell class]]) {
//        正常情況下在這里做特定cell的賦值,上面就簡單寫了
//    }
    
    return nil;
}

@end

DemoModuleADetailViewController

//
//  DemoModuleADetailViewController.h
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface DemoModuleADetailViewController : UIViewController

/// 只讀;外界只能拿到數(shù)據(jù) 但是不能修改
@property (nonatomic, strong, readonly) UILabel *valueLabel;
@property (nonatomic, strong, readonly) UIImageView *imageView;

@end


















//
//  DemoModuleADetailViewController.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "DemoModuleADetailViewController.h"
#import <HandyFrame/UIView+LayoutMethods.h>

@interface DemoModuleADetailViewController ()

/// 讀寫;重寫屬性,使 點(diǎn)m文件 中的屬性 可以被操作
@property (nonatomic, strong, readwrite) UILabel *valueLabel;
@property (nonatomic, strong, readwrite) UIImageView *imageView;

@property (nonatomic, strong) UIButton *returnButton;

@end

@implementation DemoModuleADetailViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];
    
    [self.view addSubview:self.valueLabel];
    [self.view addSubview:self.imageView];
    [self.view addSubview:self.returnButton];
}

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    
    [self.valueLabel sizeToFit];
    [self.valueLabel topInContainer:70 shouldResize:NO];
    // 橫坐標(biāo)居中
    [self.valueLabel centerXEqualToView:self.view];
//    // 等效于
//    self.valueLabel.ct_x = self.view.ct_centerX;
    
    self.imageView.ct_size = CGSizeMake(100, 100);
    [self.imageView centerEqualToView:self.view];
    
    self.returnButton.ct_size = CGSizeMake(100, 100);
    [self.returnButton bottomInContainer:0 shouldResize:NO];
    [self.returnButton centerXEqualToView:self.view];
}

#pragma mark - event response 事件響應(yīng)
- (void)didTappedReturnButton:(UIButton *)button
{
    // 判斷當(dāng)前導(dǎo)航控制器是否為空;從而得知當(dāng)前控制器彈出的方式
    
    if (self.navigationController == nil) {
        // 解散由視圖控制器模態(tài)呈現(xiàn)的視圖控制器。
        [self dismissViewControllerAnimated:YES completion:nil];
    } else {
        [self.navigationController popViewControllerAnimated:YES];
    }
}

#pragma mark - getters and setters
- (UILabel *)valueLabel
{
    if (_valueLabel == nil) {
        _valueLabel = [[UILabel alloc] init];
        _valueLabel.font = [UIFont systemFontOfSize:30];
        _valueLabel.textColor = [UIColor blackColor];
    }
    return _valueLabel;
}

- (UIImageView *)imageView
{
    if (_imageView == nil) {
        _imageView = [[UIImageView alloc] init];
        _imageView.contentMode = UIViewContentModeScaleAspectFit;
    }
    return _imageView;
}

- (UIButton *)returnButton
{
    if (_returnButton == nil) {
        _returnButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [_returnButton addTarget:self action:@selector(didTappedReturnButton:) forControlEvents:UIControlEventTouchUpInside];
        [_returnButton setTitle:@"return" forState:UIControlStateNormal];
        [_returnButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    }
    return _returnButton;
}

@end

主框架調(diào)用的TableView二級頁面

TableViewController

//
//  TableViewController.h
//  CTMediator
//
//  Created by casa on 2016/10/20.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface TableViewController : UIViewController

@end
















//
//  TableViewController.m
//  CTMediator
//
//  Created by casa on 2016/10/20.
//  Copyright ? 2016年 casa. All rights reserved.
//

#import "TableViewController.h"
#import <HandyFrame/UIView+LayoutMethods.h>
#import "CTMediator+CTMediatorModuleAActions.h"

@interface TableViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UIButton *closeButton;

@end

@implementation TableViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.view addSubview:self.tableView];
    [self.view addSubview:self.closeButton];
}

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    
    [self.tableView fillWidth];
    [self.tableView topInContainer:0 shouldResize:YES];
    [self.tableView bottomInContainer:50 shouldResize:YES];
    
    [self.closeButton fillWidth];
    [self.closeButton top:0 FromView:self.tableView];
    [self.closeButton bottomInContainer:0 shouldResize:YES];
}

- (void)dealloc
{
    // 在Controller被回收的時候,把相關(guān)的target也回收掉
    [[CTMediator sharedInstance] CTMediator_cleanTableViewCellTarget];
//    [CTMediator.sharedInstance CTMediator_cleanTableViewCellTarget];
}

#pragma mark - UITableViewDelegate
// cell即將顯示的時候
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 通過Mediator來獲取cell實(shí)例,由于target已經(jīng)被cache了,高頻調(diào)用不是問題。
    [[CTMediator sharedInstance] CTMediator_configTableViewCell:cell withTitle:@"cell title" atIndexPath:indexPath];
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 通過mediator來配置cell實(shí)例,由于target已經(jīng)被cache了,高頻調(diào)用不是問題。
    return [[CTMediator sharedInstance] CTMediator_tableViewCellWithIdentifier:@"cell" tableView:tableView];
}

#pragma mark - event response  事件響應(yīng)
// 點(diǎn)擊關(guān)閉按鈕
- (void)didTappedCloseButton:(UIButton *)button
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - getters and setters
- (UITableView *)tableView
{
    if (_tableView == nil) {
        _tableView = [[UITableView alloc] init];
        _tableView.delegate = self;
        _tableView.dataSource = self;
    }
    return _tableView;
}

- (UIButton *)closeButton
{
    if (_closeButton == nil) {
        _closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [_closeButton setTitle:@"Close" forState:UIControlStateNormal];
        [_closeButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
        // 點(diǎn)擊按鈕要執(zhí)行的方法
        [_closeButton addTarget:self action:@selector(didTappedCloseButton:) forControlEvents:UIControlEventTouchUpInside];
        _closeButton.backgroundColor = [UIColor grayColor];
    }
    return _closeButton;
}

@end

Demo鏈接

https://github.com/casatwy/CTMediator

引用

組件化架構(gòu)漫談 http://www.itdecent.cn/p/67a6004f6930

iOS應(yīng)用架構(gòu)談 組件化方案 https://casatwy.com/iOS-Modulization.html

iOS中的三種路由方案實(shí)踐 http://www.itdecent.cn/p/72d705ecc177

iOS 組件化 —— 路由設(shè)計(jì)思路分析 http://www.itdecent.cn/p/76da56b3bd55

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容