簡述CTMediator
CTMediator按照功能的結構來講,使用時需要實現CTMediator的個三部分。
1.CTMediator類:承擔總樞紐,總調度的責任
2.Target_(ModuleName)類:承擔組件對外暴漏接口功能,組件要提供什么服務,主要在它的接口聲明上進行體現
3.CTMediator+(ModuleName)分類:主要供客戶端使用,里面聲明了可以調用的組件接口。
下面詳細講解
Part1: CTMediator核心功能實現:
CTMediator主要采用target-action的方式實現組件間解耦合,本身功能完全獨立,不依賴任何組件模塊。
主要結構如下:
CTMediator作為中介者,是各個組件的進行信息通訊的中樞。
主要實現方案分兩種情況:
1.首先利用runtime進行反射,將類字符串和方法字符串轉換成類和SEL方法選擇子:
SEL action = NSSelectorFromString(@"Action_response:"); NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
然后調用cocoa touch框架提供的方法直接調用
代碼如下:
[target performSelector:action withObject:params];
2.或者使用cocoa touch提供的命令模式,將消息和消息接受者封裝成一個對象,進行執行。
首先,利用target-action生成方法簽名
然后,創建NSInvocation對象,進行執行invoke。並拿到返回的結果。
代碼如下:
利用方法簽名,NSInvocation實現
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; }
Part2: 組件對外服務接口
如果組件需要對外提供服務,就需要創建自己的接收動作類
比如ModuleA要對外提供服務。那么就要創建一個
Target_A類,然后在Target_A類的.h文件中聲明對外服務的接口,並在.m文件中進行實現。
注意:Target_A類是依賴組件的。它屬於組件的一部分。
代碼如下:
@interface Target_Mine : NSObject - (id)Action_nativeFetchSportsResultVC:(NSDictionary *)param; - (void)Action_remoteAlertSportsResultVC:(NSDictionary *)param; @end
@implementation Target_Mine - (id)Action_nativeFetchSportsResultVC:(NSDictionary *)param { UIViewController *vc = [[FZMineCoordinator sharedFZMineCoordinator] targetVCWithClassName:NSStringFromClass([FZSportsResultVC class])]; if ([vc isKindOfClass:[FZSportsResultVC class]]) { [(FZSportsResultVC *)vc configContent:param[@"title"]]; } return vc; } - (void)Action_remoteAlertSportsResultVC:(NSDictionary *)param { UIViewController *vc = [[FZMineCoordinator sharedFZMineCoordinator] targetVCWithClassName:NSStringFromClass([FZSportsResultVC class])]; if ([vc isKindOfClass:[FZSportsResultVC class]]) { [(FZSportsPlanVC *)vc configContent:param[@"title"]]; } id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate; UITabBarController *rootVC = [[appDelegate window] rootViewController]; [rootVC.childViewControllers[0] pushViewController:vc animated:YES]; } @end
Part3: CTMediator+ModuleA組件通訊實際使用類
為了實現完全解耦,這個類所有使用的所有參數全部是cocoa touch框架中定義的基本類型。
像:NSDictionary,NSString, UIImage等。
里面按照作用分,可以分為:
模塊名稱字符串,模塊本地調用方法名稱字符串,模塊遠程調用方法名稱字符串
在CTMediator+ModuleA分類文件的.h文件中,定義了供其他模塊使用的接口
在CTMediator+ModuleA分類文件的.m文件中,實現供其他模塊使用的接口,調用用CTMediator的runtime機制進行實現。
CTMediator提供的方案是我認為最好的,巧妙的使用了cocoaTouch提供的反射機制,方法簽名與命令模式,簡單又完美的解決了組件間的解耦問題。
同時因為實現是基於Object-C的特性,穩定性靠譜。
在方案不同作用類分工上,簡單明了。實現了從形式到實質上完全的解耦,同時提供了對外部appURL調用的支持。是非常完美的方案。
代碼如下:
- (IBAction)goSportsPlanDetail:(UIButton *)sender { UIViewController *vc = [[CTMediator sharedInstance] Mediator_fetchSportsPlanVC:@{@"title":[sender currentTitle]}]; [self.navigationController pushViewController:vc animated:YES]; }
CTMediator提供的方案是我認為最好的,巧妙的使用了cocoaTouch提供的反射機制,方法簽名與命令模式,簡單又完美的解決了組件間的解耦問題。
同時因為實現是基於Object-C的特性,穩定性靠譜。
在方案不同作用類分工上,簡單明了。實現了從形式到實質上完全的解耦,同時提供了對外部appURL調用的支持。是非常完美的方案。
簡述MGJRouter
蘑菇街組件化方案,采用了url-block加protocal-class的方案,url-block用於頁面跳轉,protocal-class用於組件跳轉
下面對MGJRouter的主要思路進行分析。
MGJRouter核心功能實現
Part1:
MGJRouter的url-block實現方案思路為,在路由中心維護着一張路由表,url為key, block為value。
注冊路由表時,將key和value對應保存到路由表routes中
使用時,根據URL拿到對應的block進行執行。
- (NSMutableDictionary *)routes { if (!_routes) { _routes = [[NSMutableDictionary alloc] init]; } return _routes; }
但是URL對應像UIImage,NSData這樣的非常規對象是很難傳遞的。
Part2:
蘑菇街的protocal-class實現方案思路為:
在ModuleManager內維護着一張映射表,以protocol為key,以Class為Value。
注冊映射表
[ModuleManager registerClass:ClassA forProtocol:ProtocolA]
使用映射表
[ModuleManager classForProtocol:ProtocolA]
注意:上面一一對應的關系中,類是實現了對應的協議的。所以通過協議拿到的類是可以按照protocol中聲明的方法自由使用的。
注冊步驟:
1.url-block方案注冊:
在模塊對應要展示的頁面中,在load方法中進行注冊
+ (void)load { [MGJRouter registerURLPattern:@"engineer://SportsPlanVC" toObjectHandler:^id(NSDictionary *routerParameters) { FZSportsPlanVC *planVC = [FZSportsPlanVC new]; [planVC configContent:routerParameters[@"MGJRouterParameterUserInfo"][@"title"]]; return planVC; }]; }
2.protocal-class方案注冊:
在模塊的協議實現類中進行注冊:
+ (void)load { [[FZProtocolMediator sharedFZProtocolMediator] registerProtocol:NSProtocolFromString(@"FZModuleMineProtocol") forClass:[FZModuleMineProtocolImplete class]]; }
使用步驟:
根據對應的單例獲取方式,獲取既可。
- (IBAction)mgj_goSportsPlanDetail:(UIButton *)sender { UIViewController *vc = [MGJRouter objectForURL:@"engineer://SportsPlanVC" withUserInfo:@{@"title":[sender currentTitle]}]; [self.navigationController pushViewController:vc animated:YES]; }
- (IBAction)protocol_class_goSportsPlanDetail:(UIButton *)sender { Class<FZModuleMineProtocol> class = [[FZProtocolMediator sharedFZProtocolMediator] classForProtocol:NSProtocolFromString(@"FZModuleMineProtocol")]; UIViewController *vc = [class fetchSportsPlanVC:sender.currentTitle]; [self.navigationController pushViewController:vc animated:YES]; }
MGJRouter實現方案上有些復雜,使得新手學習上有些困難,同時兩張表也增加了維護成本。
不過不可否認的是url-block和protocal-class都是非常巧妙的解耦方案。
使用效果如下:

demo地址為:
https://github.com/zhfei/Engineer
