1、蘑菇街url-block
方案
這是蘑菇街中應用的一種頁面間調用的方式,經過在啓動時註冊組件提供的服務,把調用組件使用的url
和組件提供的服務block
對應起來,保存到內存中。在使用組件的服務時,經過url
找到對應的block
,而後獲取服務。安全
具體實現代碼以下:atom
//Mediator.m 中間件 @implementation Mediator typedef void (^componentBlock) (id param); @property (nonatomic, storng) NSMutableDictionary *cache - (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk { [cache setObject:blk forKey:urlPattern]; } - (void)openURL:(NSString *)url withParam:(id)param { componentBlock blk = [cache objectForKey:url];
if (blk) blk(param); } @end
1.先在Mediator中註冊url-block
#import "Mediator.h" #import "WRBookDetailViewController.h" + (void)initComponent { [[Mediator sharedInstance] registerURLPattern:@"weread://bookDetail" toHandler:^(NSDictionary *param) { WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
[[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:detailVC animated:YES];
}]; }
2.經過url在中間件中就能找到對應的block,並執行跳轉
#import "Mediator.h" + (void)gotoDetail:(NSString *)bookId {
[[Mediator sharedInstance] openURL:@"weread://bookDetail" withParam:@{@"bookId": bookId}]; }
2、 蘑菇街protocol-class方案
針對url-block
方案沒法傳遞非正規參數,新增一種protocol-class方案url
//ProtocolMediator.m 中間件 @implementation ProtocolMediator @property (nonatomic, storng) NSMutableDictionary *protocolCache - (void)registerProtocol:(Protocol *)proto forClass:(Class)cls { NSMutableDictionary *protocolCache; [protocolCache setObject:cls forKey:NSStringFromProtocol(proto)]; } - (Class)classForProtocol:(Protocol *)proto { return protocolCache[NSStringFromProtocol(proto)]; } @end
每個組件都有一個公共Protocol文件,定義了每個組件對外提供的接口spa
//ComponentProtocol.h @protocol BookDetailComponentProtocol <NSObject> - (UIViewController *)bookDetailController:(NSString *)bookId; - (UIImage *)coverImageWithBookId:(NSString *)bookId; @end @protocol ReviewComponentProtocol <NSObject> - (UIViewController *)ReviewController:(NSString *)bookId; @end //BookDetailComponent 組件 #import "ProtocolMediator.h" #import "ComponentProtocol.h" #import "WRBookDetailViewController.h" + (void)initComponent{ [[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class]; } - (UIViewController *)bookDetailController:(NSString *)bookId { WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]]; return detailVC; } - (UIImage *)coverImageWithBookId:(NSString *)bookId { …. }
最後調用者經過 protocol 從 ProtocolMediator 拿到提供這些方法的 Class,再進行調用:code
//WRReadingViewController.m 調用者 //ReadingViewController.m #import "ProtocolMediator.h" #import "ComponentProtocol.h" + (void)gotoDetail:(NSString *)bookId { Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol]; id bookDetailComponent = [[cls alloc] init]; UIViewController *vc = [bookDetailComponent bookDetailController:bookId]; [self.navigationController pushViewController:vc animated:YES]; }
3、target-action方案
1.先設置一箇中間件公共類
@interface CTMediator : NSObject + (instancetype)sharedInstance; // 遠程App調用入口 - (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion; // 本地組件調用入口 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget; - (void)releaseCachedTargetWithTargetName:(NSString *)targetName; @end // // CTMediator.m // CTMediator // // Created by casa on 16/3/13. // Copyright © 2016年 casa. All rights reserved. // #import "CTMediator.h" @interface CTMediator () @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; } /* scheme://[target]/[action]?[params] url sample: aaa://targetA/actionB?id=1234 */ - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion { NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; NSString *urlString = [url query]; for (NSString *param in [urlString componentsSeparatedByString:@"&"]) { NSArray *elts = [param componentsSeparatedByString:@"="]; if([elts count] < 2) continue; [params setObject:[elts lastObject] forKey:[elts firstObject]]; } // 這裏這麼寫主要是出於安全考慮,防止黑客經過遠程方式調用本地模塊。這裏的作法足以應對絕大多數場景,若是要求更加嚴苛,也能夠作更加複雜的安全邏輯。 NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""]; if ([actionName hasPrefix:@"native"]) { return @(NO); } // 這個demo針對URL的路由處理很是簡單,就只是取對應的target名字和method名字,但這已經足以應對絕大部份需求。若是須要拓展,能夠在這個方法調用以前加入完整的路由邏輯 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 { NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; id target = self.cachedTarget[targetClassString]; if (target == nil) { Class targetClass = NSClassFromString(targetClassString); target = [[targetClass alloc] init]; } SEL action = NSSelectorFromString(actionString); if (target == nil) { // 這裏是處理無響應請求的地方之一,這個demo作得比較簡單,若是沒有能夠響應的target,就直接return了。實際開發過程當中是能夠事先給一個固定的target專門用於在這個時候頂上,而後處理這種請求的 return nil; } if (shouldCacheTarget) { self.cachedTarget[targetClassString] = target; } if ([target respondsToSelector:action]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" return [target performSelector:action withObject:params]; #pragma clang diagnostic pop } else { // 這裏是處理無響應請求的地方,若是無響應,則嘗試調用對應target的notFound方法統一處理 SEL action = NSSelectorFromString(@"notFound:"); if ([target respondsToSelector:action]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" return [target performSelector:action withObject:params]; #pragma clang diagnostic pop } else { // 這裏也是處理無響應請求的地方,在notFound都沒有的時候,這個demo是直接return了。實際開發過程當中,能夠用前面提到的固定的target頂上的。 [self.cachedTarget removeObjectForKey:targetClassString]; return nil; } } } - (void)releaseCachedTargetWithTargetName:(NSString *)targetName { NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; [self.cachedTarget removeObjectForKey:targetClassString]; } #pragma mark - getters and setters - (NSMutableDictionary *)cachedTarget { if (_cachedTarget == nil) { _cachedTarget = [[NSMutableDictionary alloc] init]; } return _cachedTarget; } @end
2.每一個組件建立一個Category,在其中實現當前組件的跳轉實現component
#import <CTMediator/CTMediator.h> #import <UIKit/UIKit.h> @interface CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete; @end #import "CTMediator+TAConfirmOrder.h" @implementation CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete { NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; params[@"goodsId"] = goodsId; params[@"goodsName"] = goodsName; params[@"completeBlock"] = confirmComplete; return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO]; } @end
3.經過中間件調用
UIViewController *confirmOrderVC = [[CTMediator sharedInstance] confirmOrderViewControllerWithGoodsId:self.goodsId goodsName:self.goodsName ConfirmComplete:^{orm
//...中間件
}];blog