iOS 源碼分析(一):CTMediator

本文的主要目的是分析CTMediator以及其使用

CTMediator簡(jiǎn)介

CTMediatorcasatwy開(kāi)源的一個(gè)三方組件通訊框架脸爱,下圖是通過(guò)CTMediator通訊的整體架構(gòu)圖示

整體架構(gòu)圖示

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

  • 組件僅通過(guò)Action暴露可調(diào)用接口魁淳,模塊與模塊之間的接口被固化在了Target-Action這一層,避免了實(shí)施組件化的改造過(guò)程中诊县,對(duì)Business的侵入,同時(shí)也提高了組件化接口的可維護(hù)性。

  • 方便傳遞各種類(lèi)型的參數(shù)。

  • 本地組件通訊為遠(yuǎn)程組件通訊提供服務(wù)

  • 利用 category 可以明確聲明的接口哼勇,進(jìn)行編譯檢查

  • 實(shí)現(xiàn)方式屬于輕量級(jí)

缺點(diǎn):

  • 需要給每一個(gè)模塊創(chuàng)建對(duì)應(yīng)的 target和 mediator 分類(lèi),模塊化代碼時(shí)較為繁瑣

  • 在 category 中仍然使用了字符串硬編碼呕乎,內(nèi)部使用字典傳參

  • 無(wú)法保證所調(diào)用的目標(biāo)模塊一定存在猴蹂,運(yùn)行時(shí)才能發(fā)現(xiàn)錯(cuò)誤

CTMediator原理

解耦原理

  • 通過(guò)中介模式,將CTMediator類(lèi)作為各個(gè)模塊的中心楣嘁,各個(gè)模塊以CTMediator分類(lèi)的形式擴(kuò)展功能,CTMediator分類(lèi)中提供服務(wù)

  • 分類(lèi)中的函數(shù)具體去調(diào)用target-action,target和action需要以字符串硬編碼的形式逐虚,如果模塊比較多聋溜,提供服務(wù)比較多,那么字符串的硬編碼需要時(shí)間維護(hù)叭爱。

  • 去model化思想:如果A模塊向B模塊傳遞信息撮躁,推薦參數(shù)使用基本數(shù)據(jù)類(lèi)型,而不是以model形式提供买雾,否則A模塊依賴同一個(gè)model,B模塊也依賴同一個(gè)model把曼,這樣模塊間還是存在相互依賴,沒(méi)有達(dá)到真正完全耦合漓穿。

實(shí)現(xiàn)原理

  • 基于OC的runtime嗤军、category特性動(dòng)態(tài)獲取模塊
    • 通過(guò)NSClassFromString動(dòng)態(tài)獲取類(lèi),并創(chuàng)建實(shí)例

    • 通過(guò)NSSelectorFromString動(dòng)態(tài)獲取SEL

  • 通過(guò)performSelector+NSInvocation動(dòng)態(tài)調(diào)用方法
    • performSelector響應(yīng)OC的動(dòng)態(tài)性晃危,將方法的綁定延遲到運(yùn)行時(shí)叙赚,即在編譯階段不會(huì)檢測(cè)方法的有效性(如果方法不存在也不會(huì)報(bào)錯(cuò))。

    • 如果方法名未知可能會(huì)引起內(nèi)存泄漏相關(guān)問(wèn)題僚饭,可以通過(guò)以下代碼忽略此警告

//如果方法名稱(chēng)是動(dòng)態(tài)不確定的震叮,會(huì)有如下提示
?? PerformSelector may cause a leak because its selector is unknown

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self performSelector:selector];
#pragma clang diagnostic pop
  • performSelector提供動(dòng)態(tài)執(zhí)行方法的能力

  • NSInvocation提供了消息調(diào)用的能力

  • 方法調(diào)用的本質(zhì)是消息發(fā)送,即底層調(diào)用的是objc_msgSend鳍鸵,可以從objc源碼得到驗(yàn)證

- (id)performSelector:(SEL)sel withObject:(id)obj {
  if (!sel) [self doesNotRecognizeSelector:sel];
  return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
}

源碼分析

通過(guò)閱讀CTMediator的源碼可知苇瓣,以下是源碼的通訊過(guò)程圖示


源碼的通訊過(guò)程圖示

以上是組件化方案的一個(gè)簡(jiǎn)化版架構(gòu)描述,主要是基于Mediator模式Target-Action模式偿乖,中間采用了runtime來(lái)完成調(diào)用击罪。這套組件化方案將遠(yuǎn)程應(yīng)用調(diào)用和本地應(yīng)用調(diào)用做了拆分,而且是由本地應(yīng)用調(diào)用為遠(yuǎn)程應(yīng)用調(diào)用提供服務(wù)

CTMediator調(diào)用的方式有兩種(偽代碼實(shí)現(xiàn))

  • 本地組件化通訊:本地原生模塊間的調(diào)用
- (id _Nullable )performTarget:action:params:shouldCacheTarget:{
    //拼接類(lèi)名字符串
    NSString *targetClassString = "Target_" + targetNaem;
    //從緩存中獲取類(lèi)
    NSObject *target = [self safeFetchCachedTarget:targetClassString]
    if (緩存中沒(méi)有target){
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }
    
    //拼接方法字符串
    NSString *actionString = "Action_" + actionName;
    //生成SEL
    SEL action = NSSelectorFromString(actionString);
    
    if (target為空){
        處理target為空的情況
    }
    
    //是否緩存target
    if (shouldCacheTarget) {
        //以key-value的形式緩存
        (key:target, value:targetClassString)
    }
    
    //判斷target是否響應(yīng)action
    if ([target respondsToSelector:action]) {
        return [self safePerformAction:action target:target params:params];
    }else{
        處理target未響應(yīng)action的情況
    }
}

- (id)safePerformAction:target:params:{
    //根據(jù)action生成簽名
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    //根據(jù)不同的返回類(lèi)型汹想,進(jìn)行封裝
    //根據(jù)簽名對(duì)象創(chuàng)建調(diào)用對(duì)象invocation
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
    invocation設(shè)置target外邓、action、params
    //消息調(diào)用
    [invocation invoke];
    
    //提供動(dòng)態(tài)執(zhí)行方法
    return [target performSelector:action withObject:params];
}
  • 遠(yuǎn)程組件化通訊:其他app調(diào)用古掏,主要是通過(guò)openURL的方式损话,需要在AppDelegate的openUrl代理方法中進(jìn)行處理
- (id _Nullable)performActionWithUrl:completion:{
    處理url參數(shù) - 遍歷并存放到字典中
    處理targetName,目的是為了防止黑客通過(guò)遠(yuǎn)程方式調(diào)用本地模塊
    [self performTarget:action:params:shouldCacheTarget:];
    回調(diào)處理
}

CTMediator使用

傳統(tǒng)的界面跳轉(zhuǎn)方式如下槽唾,模塊間高度耦合


傳統(tǒng)跳轉(zhuǎn)

以下是以Home丧枪、Detail模塊為例,通過(guò)CTMediator的解耦方式


通過(guò)CTMediator的解耦

以下是Demo代碼的整體結(jié)構(gòu)


整體結(jié)構(gòu)

具體的實(shí)現(xiàn)代碼如下

  • Home模塊實(shí)現(xiàn)的核心代碼如下
- (void)pushDetailAction:(UIButton *)sender{
  UIViewController *vc = [MIM() MIMediator_pushDetail];
  [self.navigationController pushViewController:vc animated:YES];
}
  • Detail模塊實(shí)現(xiàn)的核心代碼如下
- (void)showAlert{
//  [[MIMediator sharedInstance] MIMediator_showAlert];
  [MIM() MIMediator_showAlert]; 
}

可以簡(jiǎn)化組件化單例調(diào)用方式

// 簡(jiǎn)化調(diào)用單例的函數(shù)
MIMediator* _Nonnull MIMD(void);
  • 組件化通訊實(shí)現(xiàn)的代碼如下
//MIMediator+Universal中的實(shí)現(xiàn)
- (UIViewController *)MIMediator_pushDetail{
  UIViewController *vc = [self performTarget:kMIMediatorTargetDetail action:kMIMediatorActionPushDetail params:nil shouldCacheTarget:NO];
  if ([vc isKindOfClass:[UIViewController class]]) {
    return vc;
  }else{
    return [[UIViewController alloc] init];;
  }
}
- (void)MIMediator_showAlert{
  [self performTarget:kMIMediatorTargetDetail action:kMIMediatorActionShowAlert params:nil shouldCacheTarget:NO];
}

//Target_Detail中的實(shí)現(xiàn)
- (UIViewController *)Action_pushToDetail:(NSDictionary *)param{
  DetailViewController *detailVC = [[DetailViewController alloc] init];
  detailVC.title = @"詳情頁(yè)";
  return detailVC;
}
- (id)Action_showAlert:(NSDictionary *)dic{
  UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"alert title" message:@"alert message" preferredStyle:UIAlertControllerStyleAlert];
  [alertVC addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil]];
  [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVC animated:YES completion:nil];
  return nil;
}

參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庞萍,一起剝皮案震驚了整個(gè)濱河市拧烦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钝计,老刑警劉巖恋博,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件齐佳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡债沮,警方通過(guò)查閱死者的電腦和手機(jī)炼吴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疫衩,“玉大人硅蹦,你說(shuō)我怎么就攤上這事∶泼海” “怎么了童芹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鲤拿。 經(jīng)常有香客問(wèn)我假褪,道長(zhǎng),這世上最難降的妖魔是什么皆愉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任嗜价,我火速辦了婚禮,結(jié)果婚禮上幕庐,老公的妹妹穿的比我還像新娘久锥。我一直安慰自己,他們只是感情好异剥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布瑟由。 她就那樣靜靜地躺著,像睡著了一般冤寿。 火紅的嫁衣襯著肌膚如雪歹苦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天督怜,我揣著相機(jī)與錄音殴瘦,去河邊找鬼。 笑死号杠,一個(gè)胖子當(dāng)著我的面吹牛蚪腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姨蟋,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼屉凯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了眼溶?” 一聲冷哼從身側(cè)響起悠砚,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堂飞,沒(méi)想到半個(gè)月后灌旧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體绑咱,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年节榜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了羡玛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宗苍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薄榛,到底是詐尸還是另有隱情讳窟,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布敞恋,位于F島的核電站丽啡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏硬猫。R本人自食惡果不足惜补箍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啸蜜。 院中可真熱鬧坑雅,春花似錦、人聲如沸衬横。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜂林。三九已至遥诉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間噪叙,已是汗流浹背矮锈。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睁蕾,地道東北人苞笨。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惫霸,于是被迫代替她去往敵國(guó)和親猫缭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 今年項(xiàng)目進(jìn)行組件化時(shí)做了一些技術(shù)調(diào)研壹店,關(guān)于路由最終決定在 Target-Action 和 協(xié)議代理 兩個(gè)方案...
    四月_Hsu閱讀 582評(píng)論 0 3
  • 最近在學(xué)習(xí)組件化的一些方案猜丹。這里收集消化了一下,分享給大家參考硅卢。 組件化是架構(gòu)層面的一個(gè)概念射窒,它把項(xiàng)目按照某些規(guī)則...
    _常小仙兒閱讀 2,473評(píng)論 3 6
  • iOS之武功秘籍 文章匯總[http://www.reibang.com/p/07991e5b1c30] 寫(xiě)在前...
    長(zhǎng)茳閱讀 2,320評(píng)論 4 30
  • 本文主要講一樣組件化之間是如何通訊的 組件化通訊方案 目前主流的主要有以下三種方式: 1藏杖、URL路由 2、targ...
    恍然如夢(mèng)_b700閱讀 1,218評(píng)論 0 5
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月脉顿,有人笑有人哭蝌麸,有人歡樂(lè)有人憂愁,有人驚喜有人失落艾疟,有的覺(jué)得收獲滿滿有...
    陌忘宇閱讀 8,536評(píng)論 28 53