2019-04-21

iOS應用架構談 網(wǎng)絡層設計方案

前言

網(wǎng)絡層在一個App中也是一個不可缺少的部分,工程師們在網(wǎng)絡層能夠發(fā)揮的空間也比較大缆瓣。另外佑钾,蘋果對網(wǎng)絡請求部分已經(jīng)做了很好的封裝咕宿,業(yè)界的AFNetworking也被廣泛使用。其它的ASIHttpRequest蜒简,MKNetworkKit啥的其實也都還不錯瘸羡,但前者已經(jīng)棄坑,后者也在棄坑的邊緣臭蚁。在實際的App開發(fā)中最铁,Afnetworking已經(jīng)成為了事實上各大App的標準配置。

網(wǎng)絡層在一個App中承載了API調用垮兑,用戶操作日志記錄冷尉,甚至是即時通訊等任務。我接觸過一些App(開源的和不開源的)的代碼系枪,在看到網(wǎng)絡層這一塊時雀哨,尤其是在看到各位架構師各顯神通展示了各種技巧,我非常為之感到興奮。但有的時候雾棺,往往也對于其中的一些缺陷感到失望膊夹。

關于網(wǎng)絡層的設計方案會有很多,需要權衡的地方也會有很多捌浩,甚至于爭議的地方都會有很多放刨。但無論如何,我都不會對這些問題做出任何逃避尸饺,我會在這篇文章中給出我對它們的看法和解決方案进统,觀點絕不中立,不會跟大家打太極浪听。

這篇文章就主要會講這些方面:

  • 網(wǎng)絡層跟業(yè)務對接部分的設計
  • 網(wǎng)絡層的安全機制實現(xiàn)
  • 網(wǎng)絡層的優(yōu)化方案

網(wǎng)絡層跟業(yè)務對接部分的設計

在安居客App的架構更新?lián)Q代的時候螟碎,我深深地感覺到網(wǎng)絡層跟業(yè)務對接部分的設計有多么重要,因此我對它做的最大改變就是針對網(wǎng)絡層跟業(yè)務對接部分的改變迹栓。網(wǎng)絡層跟業(yè)務層對接部分設計的好壞掉分,會直接影響到業(yè)務工程師實現(xiàn)功能時的心情。

在正式開始講設計之前克伊,我們要先討論幾個問題:

  • 使用哪種交互模式來跟業(yè)務層做對接酥郭?
  • 是否有必要將API返回的數(shù)據(jù)封裝成對象然后再交付給業(yè)務層?
  • 使用集約化調用方式還是離散型調用方式去調用API答毫?

這些問題討論完畢之后褥民,我會給出一個完整的設計方案來給大家做參考,設計方案是魚洗搂,討論的這些問題是漁消返,我什么都授了,大家各取所需耘拇。

使用哪種交互模式來跟業(yè)務層做對接撵颊?

這里其實有兩個問題:

  • 以什么方式將數(shù)據(jù)交付給業(yè)務層?
  • 交付什么樣的數(shù)據(jù)給業(yè)務層惫叛?

以什么方式將數(shù)據(jù)交付給業(yè)務層倡勇?

iOS開發(fā)領域有很多對象間數(shù)據(jù)的傳遞方式,我看到的大多數(shù)App在網(wǎng)絡層所采用的方案主要集中于這三種:Delegate嘉涌,Notification妻熊,Block。KVO和Target-Action我目前還沒有看到有使用的仑最。

目前我知道邊鋒主要是采用的block扔役,大智慧主要采用的是Notification,安居客早期以Block為主警医,后面改成了以Delegate為主亿胸,阿里沒發(fā)現(xiàn)有通過Notification來做數(shù)據(jù)傳遞的地方(可能有)坯钦,Delegate、Block以及target-action都有侈玄,阿里iOS App網(wǎng)絡層的作者說這是為了方便業(yè)務層選擇自己合適的方法去使用婉刀。這里大家都是各顯神通,每次我看到這部分的時候序仙,我都喜歡問作者為什么采用這種交互方案突颊,但很少有作者能夠說出個條條框框來。

然而在我這邊潘悼,我的意見是以Delegate為主洋丐,Notification為輔。原因如下:

  • 盡可能減少跨層數(shù)據(jù)交流的可能挥等,限制耦合
  • 統(tǒng)一回調方法,便于調試和維護
  • 在跟業(yè)務層對接的部分只采用一種對接手段(在我這兒就是只采用delegate這一個手段)限制靈活性堤尾,以此來交換應用的可維護性

盡可能減少跨層數(shù)據(jù)交流的可能肝劲,限制耦合

什么叫跨層數(shù)據(jù)交流?就是某一層(或模塊)跟另外的與之沒有直接對接關系的層(或模塊)產(chǎn)生了數(shù)據(jù)交換郭宝。為什么這種情況不好辞槐?嚴格來說應該是大部分情況都不好,有的時候跨層數(shù)據(jù)交流確實也是一種需求粘室。之所以說不好的地方在于榄檬,它會導致代碼混亂,破壞模塊的封裝性衔统。我們在做分層架構的目的其中之一就在于下層對上層有一次抽象鹿榜,讓上層可以不必關心下層細節(jié)而執(zhí)行自己的業(yè)務。

所以锦爵,如果下層細節(jié)被跨層暴露舱殿,一方面你很容易因此失去鄰層對這個暴露細節(jié)的保護;另一方面险掀,你又不可能不去處理這個細節(jié)沪袭,所以處理細節(jié)的相關代碼就會散落各地,最終難以維護樟氢。

說得具象一點就是冈绊,我們考慮這樣一種情況:A<-B<-C。當C有什么事件埠啃,通過某種方式告知B死宣,然后B執(zhí)行相應的邏輯。一旦告知方式不合理霸妹,讓A有了跨層知道C的事件的可能十电,你 就很難保證A層業(yè)務工程師在將來不會對這個細節(jié)作處理。一旦業(yè)務工程師在A層產(chǎn)生處理操作,有可能是補充邏輯鹃骂,也有可能是執(zhí)行業(yè)務台盯,那么這個細節(jié)的相關處理代碼就會有一部分散落在A層。然而前者是不應該散落在A層的畏线,后者有可能是需求静盅。另外,因為B層是對A層抽象的寝殴,執(zhí)行補充邏輯的時候蒿叠,有可能和B層針對這個事件的處理邏輯產(chǎn)生沖突,這是我們很不希望看到的蚣常。

那么什么情況跨層數(shù)據(jù)交流會成為需求市咽?在網(wǎng)絡層這邊,信號從2G變成3G變成4G變成Wi-Fi抵蚊,這個是跨層數(shù)據(jù)交流的其中一個需求施绎。不過其他的跨層數(shù)據(jù)交流需求我暫時也想不到了,哈哈贞绳,應該也就這一個吧谷醉。


嚴格來說,使用Notification來進行網(wǎng)絡層和業(yè)務層之間數(shù)據(jù)的交換冈闭,并不代表這一定就是跨層數(shù)據(jù)交流俱尼,但是使用Notification給跨層數(shù)據(jù)交流開了一道口子,因為Notification的影響面不可控制萎攒,只要存在實例就存在被影響的可能遇八。另外,這也會導致誰都不能保證相關處理代碼就在唯一的那個地方耍休,進而帶來維護災難押蚤。作為架構師,在這里給業(yè)務工程師限制其操作的靈活性是必要的羹应。另外揽碘,Notification也支持一對多的情況,這也給代碼散落提供了條件园匹。同時雳刺,Notification所對應的響應方法很難在編譯層面作限制,不同的業(yè)務工程師會給他取不同的名字裸违,這也會給代碼的可維護性帶來災難掖桦。

手機淘寶架構組的俠武同學曾經(jīng)給我分享過一個問題,在這里我也分享給大家:曾經(jīng)有一個工程師在監(jiān)聽Notification之后供汛,沒有寫釋放監(jiān)聽的代碼枪汪,當然涌穆,找到這個原因又是很漫長的一段故事,現(xiàn)在找到原因了雀久,然而監(jiān)聽這個Notification的對象有那么多宿稀,不知道具體是哪個Notificaiton,也不知道那個沒釋放監(jiān)聽的對象是誰赖捌。后來折騰了很久大家都沒辦法的時候祝沸,有一個經(jīng)驗豐富的工程師提出用hook(Method Swizzling)的方式,最終找到了那個沒釋放監(jiān)聽的對象越庇,bug修復了罩锐。

我分享這個問題的目的并不是想強調Notification多么多么不好,Notification本身就是一種設計模式卤唉,在屬于他的問題領域內(nèi)涩惑,Notification是非常好的一種解決方案。但我想強調的是桑驱,對于網(wǎng)絡層這個問題領域內(nèi)來看境氢,架構師首先一定要限制代碼的影響范圍,在能用影響范圍小的方案的時候就盡量采用這種小的方案碰纬,否則將來要是有什么奇怪需求或者出了什么小問題,維護起來就非常麻煩问芬。因此Notification這個方案不能作為首選方案悦析,只能作為備選。

那么Notification也不是完全不能使用此衅,當需求要求跨層時强戴,我們就可以使用Notification,比如前面提到的網(wǎng)絡條件切換挡鞍,而且這個需求也是需要滿足一對多的骑歹。

所以,為了符合前面所說的這些要求墨微,使用Delegate能夠很好地避免跨層訪問道媚,同時限制了響應代碼的形式,相比Notification而言有更好的可維護性翘县。


然后我們順便來說說為什么盡量不要用block最域。

block很難追蹤,難以維護
我們在調試的時候經(jīng)常會單步追蹤到某一個地方之后锈麸,發(fā)現(xiàn)尼瑪這里有個block镀脂,如果想知道這個block里面都做了些什么事情,這時候就比較蛋疼了忘伞。


- (void)someFunctionWithBlock:(SomeBlock *)block
{
    ... ...

 -> block();  //當你單步走到這兒的時候薄翅,要想知道block里面都做了哪些事情的話沙兰,就很麻煩。

    ... ...
}

block會延長相關對象的生命周期

block會給內(nèi)部所有的對象引用計數(shù)加一翘魄,這一方面會帶來潛在的retain cycle鼎天,不過我們可以通過Weak Self的手段解決。另一方面比較重要就是熟丸,它會延長對象的生命周期训措。

在網(wǎng)絡回調中使用block,是block導致對象生命周期被延長的其中一個場合光羞,當ViewControllerwindow中卸下時绩鸣,如果尚有請求帶著block在外面飛,然后block里面引用了ViewController(這種場合非常常見)纱兑,那么ViewController是不能被及時回收的呀闻,即便你已經(jīng)取消了請求,那也還是必須得等到請求著陸之后才能被回收潜慎。

然而使用delegate就不會有這樣的問題捡多,delegate是弱引用,哪怕請求仍然在外面飛铐炫,垒手,ViewController還是能夠及時被回收的,回收之后指針自動被置為了nil倒信,無傷大雅科贬。

block在離散型場景下不符合使用的規(guī)范
blockdelegate乍看上去在作用上是很相似,但是關于它們的選型有一條嚴格的規(guī)范:當回調之后要做的任務在每次回調時都是一致的情況下鳖悠,選擇delegate榜掌,在回調之后要做的任務在每次回調時無法保證一致,選擇block乘综。在離散型調用的場景下憎账,每一次回調都是能夠保證任務一致的,因此適用delegate卡辰。這也是蘋果原生的網(wǎng)絡調用也采用delegate的原因胞皱,因為蘋果也是基于離散模型去設計網(wǎng)絡調用的,而且本文即將要介紹的網(wǎng)絡層架構也是基于離散型調用的思路去設計的九妈。

在集約型調用的場景下朴恳,使用block是合理的,因為每次請求的類型都不一樣允蚣,那么自然回調要做的任務也都會不一樣于颖,因此只能采用blockAFNetworking就是屬于集約型調用嚷兔,因此它采用了block來做回調森渐。

就我所知做入,目前大部分公司的App網(wǎng)絡層都是集約型調用,因此廣泛采取了block回調同衣。但是在App的網(wǎng)絡層架構設計中直接采用集約型調用來為業(yè)務服務的思路是有問題的竟块,因此在遷移到離散型調用時,一定要注意這一點耐齐,記得遷回delegate回調浪秘。關于離散型和集約型調用的介紹和如何選型,我在后面的集約型API調用方式和離散型API調用方式的選擇埠况?小節(jié)中有詳細的介紹耸携。

所以平時盡量不要濫用block,尤其是在網(wǎng)絡層這里辕翰。

統(tǒng)一回調方法夺衍,便于調試和維護

前面講的是跨層問題,區(qū)分了DelegateNotification喜命,順帶談了一下Block沟沙。然后現(xiàn)在談到的這個情況,就是另一個采用Block方案不是很合適的情況壁榕。首先矛紫,Block本身無好壞對錯之分,只有合適不合適牌里。在這一節(jié)要講的情況里颊咬,Block無法做到回調方法的統(tǒng)一,調試和維護的時候也很難在調用棧上顯示出來二庵,找的時候會很蛋疼。

在網(wǎng)絡請求和網(wǎng)絡層接受請求的地方時缓呛,使用Block沒問題催享。但是在獲得數(shù)據(jù)交給業(yè)務方時,最好還是通過Delegate去通知到業(yè)務方哟绊。因為Block所包含的回調代碼跟調用邏輯放在同一個地方因妙,會導致那部分代碼變得很長,因為這里面包括了調用前和調用后的邏輯票髓。從另一個角度說攀涵,這在一定程度上違背了single function,single task的原則洽沟,在需要調用API的地方,就只要寫API調用相關的代碼,在回調的地方送丰,寫回調的代碼。

然后我看到大部分App里炉媒,當業(yè)務工程師寫代碼寫到這邊的時候昆烁,也意識到了這個問題静尼。因此他們會在block里面寫個一句話的方法接收參數(shù),然后做轉發(fā)鸭巴,然后就可以把這個方法放在其他地方了奕扣,繞過了Block的回調著陸點不統(tǒng)一的情況掌敬。比如這樣:

  [API callApiWithParam:param successed:^(Response *response){
        [self successedWithResponse:response];
    } failed:^(Request *request, NSError *error){
        [self failedWithRequest:request error:error];
    }];
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奔害,一起剝皮案震驚了整個濱河市华临,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揭厚,老刑警劉巖扶供,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椿浓,死亡現(xiàn)場離奇詭異,居然都是意外死亡扳碍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門碱蒙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夯巷,“玉大人巧还,你說我怎么就攤上這事麸祷“Γ” “怎么了星瘾?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵琳状,是天一觀的道長念逞。 經(jīng)常有香客問我,道長硕盹,這世上最難降的妖魔是什么瘩例? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任甸各,我火速辦了婚禮,結果婚禮上趣倾,老公的妹妹穿的比我還像新娘聘惦。我一直安慰自己,他們只是感情好誊酌,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布露乏。 她就那樣靜靜地躺著碧浊,像睡著了一般瘟仿。 火紅的嫁衣襯著肌膚如雪箱锐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天驹止,我揣著相機與錄音浩聋,去河邊找鬼。 笑死臊恋,一個胖子當著我的面吹牛衣洁,可吹牛的內(nèi)容都是我干的抖仅。 我是一名探鬼主播坊夫,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼环凿,長吁一口氣:“原來是場噩夢啊……” “哼放吩!你這毒婦竟也來了到推?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悔雹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欣喧,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡腌零,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唆阿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片益涧。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驯鳖,靈堂內(nèi)的尸體忽然破棺而出闲询,到底是詐尸還是另有隱情,我是刑警寧澤浅辙,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布扭弧,位于F島的核電站,受9級特大地震影響记舆,放射性物質發(fā)生泄漏鸽捻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望御蒲。 院中可真熱鬧衣赶,春花似錦、人聲如沸厚满。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碘箍。三九已至摘能,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敲街,已是汗流浹背团搞。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留多艇,地道東北人逻恐。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像峻黍,于是被迫代替她去往敵國和親复隆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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