簡述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都是非常巧妙的解耦方案。
使用效果如下: