5. 中間件組件化

在上篇文章我們討論一下,url方式和缺點(diǎn),我們現(xiàn)在來討論通過,中間件來實(shí)現(xiàn)解耦。 主要是參考 CTMediator,來實(shí)現(xiàn)我們的組件化。筆者考慮到CTMediator 當(dāng)做一個(gè)單例來處理(內(nèi)部有個(gè)target緩存),我不太想這樣處理,我覺得Mediator 就是一個(gè) 工具類,通過傳遞 組件的target 和 sel,其實(shí)就是一個(gè)加方法 就可以的。

中間件代碼

這個(gè)中間件,其實(shí)就是一個(gè)動(dòng)態(tài)解析的過程,把哪些組件的使用放到動(dòng)態(tài)解析中,這里就不用注冊(cè)了。

下面是筆者改造后的中間件,主要有三部分組成

  • target: 目標(biāo)組件,我們一邊抽離一個(gè)工具類(使用 門面模式)
  • action: 調(diào)用方法
  • param: 傳遞參數(shù),和使用回調(diào)

代碼如下

//傳遞回調(diào)值
typedef void(^ZLMediatorCallBack)(id param);
extern NSString * const ZLMediatorCallBackKey; //可以放在 param 中

@interface ZLMediator : NSObject

// 本地組件調(diào)用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName
                      actionName:(NSString *)actionName
                          params:(NSDictionary *)params;


@end



NSString * const ZLMediatorCallBackKey = @"ZLMediatorCallBackKey";
@implementation ZLMediator
// 本地組件調(diào)用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
    
    Class targetClass = targetName.length > 0 ? NSClassFromString(targetName) : nil;
    NSString * msg = [NSString stringWithFormat:@"不存在Target類名為:%@",targetName];
    NSAssert(targetClass, msg);
    
    SEL action = actionName.length > 0 ?  NSSelectorFromString(actionName) : nil;
    msg = [NSString stringWithFormat:@"Target_%@ 不存在 action為:%@",targetName,actionName];
    NSAssert(action, msg);
    
    if ([targetClass respondsToSelector:action]) {
        return [self SafePerformAction:action target:targetClass params:params];
    } else {
        msg = [NSString stringWithFormat:@"Target_%@ 不能響應(yīng) action_%@",targetName,actionName];
        NSAssert(0, msg);
    }
    return nil;
}


+ (id)SafePerformAction:(SEL)action target:(id)target params:(NSDictionary *)params {
    
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    const char* retType = [methodSig methodReturnType];
    
    if (strcmp(retType, @encode(void)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        return nil;
    }
    
    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;
        [invocation getReturnValue:&result];
        return @(result);
    }
    
    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);
    }
    
    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);
    }
    
    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);
    }
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}

@end

方便使用中間件

1.使用門面模式來暴露組件的使用

@interface Detail_Target : NSObject
+ (void)Target_showWithParam:(NSDictionary *)param;
@end

// 門面模式 定義方法
@implementation Detail_Target
+ (void)Target_showWithParam:(NSDictionary *)param {
    DetailComposite2 * detail = [[DetailComposite2 alloc] init];
    detail.oneId = param[@"id"];
    detail.name  = param[@"name"];
    // 執(zhí)行組件的方法
    [detail showComposite];
}
@end

然后使用就可以

[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
                                actionName:@"Target_showWithParam:"
                                    params:@{@"id":@"1", @"name":@"leeDev"}];
//打印出 showComposite2 _ id = 1 ; name = leeDev

Mediator 擴(kuò)展

但是這樣傳遞參數(shù)還是比較麻煩,所用我們可以使用 Media category 來簡化我們的調(diào)用,讓使用者更加明確

@interface ZLMediator (Detail)
//直接把 target 和 sel 和param 給屏蔽了,只給外界暴露 簡單的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name;
@end

@implementation ZLMediator (Detail)

//直接把 target 和 sel 和param 給屏蔽了,只給外界暴露 簡單的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name {
    NSDictionary * param = @{@"id":id, @"name":name};
    [ZLMediator Mediator_PerformTargetName:@"Detail_Target"
                                actionName:@"Target_showWithParam:"
                                    params:param];
}

@end

測(cè)試和使用

[ZLMediator detailShowWithId:@"10" name:@"leeDev"];
// 打印出 showComposite2 _ id = 10 ; name = leeDev

顯然相當(dāng)于上一種方法直接調(diào)用,這個(gè)方法要簡單明確多了,直接屏蔽了 target 和 sel 和param.

優(yōu)缺點(diǎn)

相對(duì)于蘑菇街的路由和協(xié)議方式的架構(gòu),這個(gè)方式要強(qiáng)大多了

  • 可以傳遞任意值
  • 不需要注冊(cè),浪費(fèi)內(nèi)存
  • 可以通過Mediator擴(kuò)展,來定義更加清晰的接口給外界使用。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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