組件化之中間件路由BSRouter

1、這里是參考CTMediator實(shí)現(xiàn)的BSRouter,用runtime的方式動(dòng)態(tài)獲取url中傳遞的類和方法名字,通過(guò)組件配置好的target類實(shí)現(xiàn)組件間的值傳遞和頁(yè)面跳轉(zhuǎn)。

2、先說(shuō)使用方式

在組件中創(chuàng)建一個(gè)對(duì)應(yīng)的target類里邊設(shè)置好對(duì)外暴露的跳轉(zhuǎn)對(duì)應(yīng)頁(yè)面的方法action。

這樣其他人通過(guò)閱讀你寫(xiě)的readMe文件知道這個(gè)組件跳轉(zhuǎn)到那些頁(yè)面需要拼接的URL是什么,這只是簡(jiǎn)單的實(shí)現(xiàn),優(yōu)化后可以通過(guò)頁(yè)面ID更優(yōu)雅的配置和跳轉(zhuǎn)頁(yè)面。

下邊這段就是簡(jiǎn)單配置好之后 頁(yè)面的跳轉(zhuǎn)
代碼塊

UIViewController *viewController = [BSR() performActionWithUrl:[NSURL URLWithString:@"bs://Home/showVCWithParams?id=1234"] completion:nil];

[UIApplication sharedApplication].keyWindow.rootViewController = viewController;

3、說(shuō)下這個(gè)url的具體含義

bs://

沒(méi)有實(shí)際價(jià)值,可以用來(lái)規(guī)范 這就是一個(gè)正規(guī)的url

Home 對(duì)應(yīng)target的類名 類名是Target_Home

showVCWithParams 是對(duì)應(yīng)的方法名action 方法名是 Action_showVCWithParams:

?后邊是傳遞的值 有了一套規(guī)范就可以傳遞數(shù)據(jù)了。

4、既然說(shuō)完了調(diào)用方式,再說(shuō)說(shuō)使用Target的創(chuàng)建以Target_Home只實(shí)現(xiàn)了HomePageViewController這個(gè)頁(yè)面的跳轉(zhuǎn)。外部直接調(diào)取bs://Home/showVCWithParams?id=1234 就可以跳進(jìn)來(lái)。

這個(gè)類就是負(fù)責(zé)吧url轉(zhuǎn)化成控制器返回給調(diào)用者。
代碼塊

@interface Target_Home : NSObject

- (UIViewController *)Action_showVCWithParams:(NSDictionary *)params;

@end

- (UIViewController *)Action_showVCWithParams:(NSDictionary *)params

{

 // 因?yàn)閍ction是從屬于ModuleA的,所以action直接可以使用ModuleA里的所有聲明

 HomePageViewController *viewController = [[HomePageViewController alloc] init];

// viewController.userID = params[@"id"];

 return viewController;

}

5、下邊是BSRouter的實(shí)現(xiàn)
代碼塊

//單例

+ (instancetype)sharedRouter;

// 遠(yuǎn)程App調(diào)用入口

- (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion;

// 本地組件調(diào)用入口

- (id _Nullable )performTarget:(NSString * _Nullable)targetName action:(NSString * _Nullable)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget;

- (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName;

// 簡(jiǎn)化調(diào)用單例的函數(shù)

BSRouter* _Nonnull BSR(void);

/*

 scheme://[target]/[action]?[params]

 url sample:

 aaa://targetA/actionB?id=1234

 */

- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion

{

 if (url == nil||![url isKindOfClass:[NSURL class]]) {

 return nil;

 }

 NSMutableDictionary *params = [[NSMutableDictionary alloc] init];

 NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString];

 // 遍歷所有參數(shù)

 [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

 if (obj.value&&obj.name) {

 [params setObject:obj.value forKey:obj.name];

 }

 }];

 // 這里這么寫(xiě)主要是出于安全考慮,防止黑客通過(guò)遠(yuǎn)程方式調(diào)用本地模塊。這里的做法足以應(yīng)對(duì)絕大多數(shù)場(chǎng)景,如果要求更加嚴(yán)苛,也可以做更加復(fù)雜的安全邏輯。

 NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];

 if ([actionName hasPrefix:@"native"]) {

 return @(NO);

 }

 // 這個(gè)demo針對(duì)URL的路由處理非常簡(jiǎn)單,就只是取對(duì)應(yīng)的target名字和method名字,但這已經(jīng)足以應(yīng)對(duì)絕大部份需求。如果需要拓展,可以在這個(gè)方法調(diào)用之前加入完整的路由邏輯

 id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];

 if (completion) {

 if (result) {

 completion(@{@"result":result});

 } else {

 completion(nil);

 }

 }

 return result;

}

- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget

{

 if (targetName == nil || actionName == nil) {

 return nil;

 }

 NSString *swiftModuleName = params[kSwiftTargetModuleName];

 // generate target

 NSString *targetClassString = nil;

 if (swiftModuleName.length > 0) {

 targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];

 } else {

 targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];

 }

 NSObject *target = [self safeFetchCachedTarget:targetClassString];

 if (target == nil) {

 Class targetClass = NSClassFromString(targetClassString);

 target = [[targetClass alloc] init];

 }

 // generate action

 NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];

 SEL action = NSSelectorFromString(actionString);

 if (target == nil) {

 // 這里是處理無(wú)響應(yīng)請(qǐng)求的地方之一,這個(gè)demo做得比較簡(jiǎn)單,如果沒(méi)有可以響應(yīng)的target,就直接return了。實(shí)際開(kāi)發(fā)過(guò)程中是可以事先給一個(gè)固定的target專門(mén)用于在這個(gè)時(shí)候頂上,然后處理這種請(qǐng)求的

 [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];

 return nil;

 }

 if (shouldCacheTarget) {

 [self safeSetCachedTarget:target key:targetClassString];

 }

 if ([target respondsToSelector:action]) {

 return [self safePerformAction:action target:target params:params];

 } else {

 // 這里是處理無(wú)響應(yīng)請(qǐng)求的地方,如果無(wú)響應(yīng),則嘗試調(diào)用對(duì)應(yīng)target的notFound方法統(tǒng)一處理

 SEL action = NSSelectorFromString(@"notFound:");

 if ([target respondsToSelector:action]) {

 return [self safePerformAction:action target:target params:params];

 } else {

 // 這里也是處理無(wú)響應(yīng)請(qǐng)求的地方,在notFound都沒(méi)有的時(shí)候,這個(gè)demo是直接return了。實(shí)際開(kāi)發(fā)過(guò)程中,可以用前面提到的固定的target頂上的。

 [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];

 @synchronized (self) {

              [self.cachedTarget removeObjectForKey:targetClassString];

 }

 return nil;

 }

 }

}

- (void)releaseCachedTargetWithFullTargetName:(NSString *)fullTargetName

{

 /*

 fullTargetName在oc環(huán)境下,就是Target_XXXX。要帶上Target_前綴。在swift環(huán)境下,就是XXXModule.Target_YYY。不光要帶上Target_前綴,還要帶上模塊名。

 */

 if (fullTargetName == nil) {

 return;

 }

 @synchronized (self) {

 [self.cachedTarget removeObjectForKey:fullTargetName];

 }

}

#pragma mark - private methods

- (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];

}

- (id)safePerformAction:(SEL)action target:(NSObject *)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:?ms 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:?ms 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:?ms 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:?ms 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:?ms 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

}

BSRouter* _Nonnull BSR(void){

 return [BSRouter sharedRouter];

};
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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