- 對于網(wǎng)絡(luò)層的設(shè)計(jì)狮荔,相信大家都不會(huì)陌生胎撇,最知名當(dāng)屬于AFNetworking。但是我們實(shí)際開發(fā)過程中網(wǎng)絡(luò)層的任務(wù)不僅僅需要單純的網(wǎng)絡(luò)請求轴合,而是需要處理關(guān)于請求數(shù)據(jù)相關(guān)的整個(gè)一大塊创坞,例如:數(shù)據(jù)緩存、日志受葛、code校對、錯(cuò)誤分析等等偎谁。我們希望網(wǎng)絡(luò)層能起到后臺(tái)與實(shí)際業(yè)務(wù)的橋梁作用总滩,同時(shí)網(wǎng)絡(luò)層不可以染指業(yè)務(wù)邏輯⊙灿辏考慮低耦合要求闰渔,我們往往會(huì)把網(wǎng)絡(luò)層設(shè)計(jì)成一個(gè)獨(dú)立模塊。而我們大名頂頂?shù)腁FNetworking并不能實(shí)現(xiàn)這一多功能要求铐望,那么我們就需要對AFNetworking二次封裝增加新的功能以滿足我們所需要的網(wǎng)絡(luò)層要求冈涧。
一、如何設(shè)計(jì)(個(gè)人淺談)
- 1.封裝形式:對AFNetworking封裝分為兩種形式正蛙,block(集約型)和代理(離散型)督弓。所以我們需要對這兩種進(jìn)行選擇。
- 2.緩存模塊:業(yè)務(wù)有時(shí)候需要一些重要對數(shù)據(jù)能夠緩存下來而不是每次請求網(wǎng)絡(luò)乒验。所以我們需要一個(gè)緩存數(shù)據(jù)的模塊愚隧。
- 3.日志系統(tǒng):對于每次請求我們不可能說每次都能一次性搞定,而是需要跟后臺(tái)不斷調(diào)試锻全,包括我們自己調(diào)試狂塘,這樣我們就需要一個(gè)日志模塊,這個(gè)模塊能夠打印出關(guān)于請求的所有信息鳄厌,成功能夠打印出正確數(shù)據(jù)荞胡,失敗能夠打印具體的失敗信息等,我們只需要簡單看看日志就能發(fā)現(xiàn)問題所在而不是通過LLDB打斷點(diǎn)一步一步調(diào)試找出錯(cuò)誤了嚎。
- 4.低偶合性泪漂。網(wǎng)絡(luò)層必須是一個(gè)獨(dú)立的模塊,不能夠跟業(yè)務(wù)邏輯糾纏在一起新思。對于網(wǎng)絡(luò)層來講窖梁,我只給你業(yè)務(wù)提供數(shù)據(jù),至于你要用數(shù)據(jù)去干什么夹囚,我并不關(guān)心纵刘。另一方面,網(wǎng)絡(luò)層必須跟AFNetworking低耦合荸哟,一旦哪天我們不想要用AFNetworking可以輕松替換假哎。
- 5:可操控性:很多時(shí)候會(huì)出現(xiàn)這種情況瞬捕,我們給的請求參數(shù)明顯不對,或者后臺(tái)返回的數(shù)據(jù)明顯不是業(yè)務(wù)需求數(shù)據(jù)舵抹。如果我們不加以處理肪虎,那么就會(huì)導(dǎo)致請求錯(cuò)誤,并且這個(gè)錯(cuò)誤數(shù)據(jù)流入地業(yè)務(wù)端惧蛹,業(yè)務(wù)端首先需要去甄別數(shù)據(jù)的好壞扇救,其次再加以使用。一旦業(yè)務(wù)的請求接口過多勢必會(huì)導(dǎo)致業(yè)務(wù)層壓力增大香嗓。對此迅腔,我們需要將這些不必要的錯(cuò)誤扼殺在網(wǎng)絡(luò)層, 所以一些時(shí)候網(wǎng)絡(luò)層需要對請求的參數(shù)靠娱,以及返回的數(shù)據(jù)進(jìn)行校對沧烈,或者修訂。這樣可以減少業(yè)務(wù)層的壓力負(fù)擔(dān)像云,使其能更好的關(guān)注業(yè)務(wù)邏輯锌雀。
- 6.簡單,易懂的代碼:有些簡單的請求如果進(jìn)行一系列分析的話有會(huì)導(dǎo)致代碼看上去很煩亂迅诬,所以我們還需要提供一些簡單易懂的調(diào)用方法腋逆。
- 7.請求控制:一些沒有必要的請求我們需要及時(shí)去除。例如:切換頁面后百框,請求還在進(jìn)行闲礼,這時(shí)候請求已經(jīng)沒有必要了,我們需要停止铐维,防止流量的浪費(fèi)柬泽,同時(shí)對于一些麻煩的請求(大數(shù)據(jù)的上傳下載等)還可以提升流暢度。
二嫁蛇、模式缺陷
1.block形式的缺陷
- block不夠安全锨并,稍微不注意就形成循環(huán)引用,導(dǎo)致對象釋放不了睬棚。這種循環(huán)引用第煮,一旦出現(xiàn)就比較難檢查出來。
- block很難追蹤抑党,難以維護(hù)
- block會(huì)延長相關(guān)對象的生命周期
- 在多通信時(shí)候包警,block顯得不夠直觀也不易維護(hù)
- block效率低,block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存
2.block形式的優(yōu)點(diǎn)
- 使用更簡單底靠,易維護(hù)害晦,能夠直接訪問上下文,獲取數(shù)據(jù)方便
- 省去了寫代理的很多代碼
3.delegate形式的優(yōu)點(diǎn)
- delegate更安全暑中, delegate 的方法是分離開的壹瘟,不會(huì)引用上下文鲫剿,不容易循環(huán)引用
- 代碼的連貫性不是很好,沒有 block 好讀
- delegate 讓多個(gè)方法分成一組稻轨,只需要設(shè)置一次灵莲,就可以多次回調(diào)。即同一個(gè)請求可能會(huì)多次調(diào)用的時(shí)候delegate只需要設(shè)置一次殴俱,而block需要多次政冻,這種情況下也推薦用delegate形式
- delegate運(yùn)行成本低,delegate只是保存了一個(gè)對象指針线欲,直接回調(diào)赠幕,沒有額外消耗
3.delegate形式的缺點(diǎn)
- 使用繁瑣,請求接口數(shù)量多的時(shí)候delegate要寫更多的代碼询筏。
- 方法的聲明和實(shí)現(xiàn)分離開來,代碼的連貫性不是很好竖慧。
- 需要臨時(shí)存儲(chǔ)很多數(shù)據(jù)嫌套,跟業(yè)務(wù)交接不方便
三、個(gè)人方案
1.業(yè)務(wù)分析
- 開始公司業(yè)務(wù)量不多的時(shí)候圾旨,接口較少踱讨,也比較簡單。自己用的都是block形式砍的,輕型痹筛、簡單、代碼易讀易修改廓鞠。這樣一方面符合剛開始的快速迭代帚稠,二方面也節(jié)省時(shí)間。但是隨著后來業(yè)務(wù)量的增加床佳,業(yè)務(wù)功能的細(xì)致化后發(fā)現(xiàn)滋早,自己的業(yè)務(wù)層開始變的非常臃腫,對于一些接口砌们,業(yè)務(wù)還需要去自己檢測數(shù)據(jù)的格式符不符合要求杆麸。一些相同的請求,多次不同地方調(diào)用浪感,block就的在多個(gè)地方多次重寫等等導(dǎo)致業(yè)務(wù)層再次變的繁瑣...
2.問題處理 - XXHNetwork
- 對于相同接口多次調(diào)用等問題我們應(yīng)該使用delegate模式昔头,但由于之前使用大量的block一時(shí)間全部更換不現(xiàn)實(shí),而且一些簡單的接口更換后反而顯得臃腫影兽,所以最終采用但是混合模式揭斧,即網(wǎng)絡(luò)層最外層即提供block形式接口,同時(shí)也提供delegate形式接口赢笨,至于需要用哪種形式未蝌,可由開發(fā)人員根據(jù)業(yè)務(wù)需求自行選擇驮吱,以達(dá)到最優(yōu)效果。
四萧吠、XXHNetwork封裝緩存左冬、日志輸出、參數(shù)纸型,返回?cái)?shù)據(jù)攔截控制
1.數(shù)據(jù)緩存
XXHNetwork提供了獨(dú)立的緩存模塊(XHNetworkCacheManager)該模塊底層運(yùn)用的是YYCache,因?yàn)閅YCache LRU算法比較符合我們現(xiàn)實(shí)的使用場景拇砰,當(dāng)然我們系統(tǒng)提供的NSCache也是ok的,看個(gè)人喜好吧狰腌。緩存模塊提供下面功能
- 支持最大緩存數(shù)量
- 緩存版本控制除破、app版本檢測
- 根據(jù)請求shouldAllIgnoreCache判斷是否需要緩存
- 緩存的有效時(shí)長
- 內(nèi)存、磁盤兩中存儲(chǔ)方式
- 緩存數(shù)據(jù)增琼腔、刪瑰枫、改、查
2.日志輸出
日志輸出由獨(dú)立的模塊XHNetworkLogManager控制丹莲,里面提供各種情況(成功光坝、失敗)下日志輸出接口甥材,只需要調(diào)用初始化方法傳入相應(yīng)數(shù)據(jù)即可盯另!
- func appendURL:用于打印請求head、body信息
- func logDebugInfo:用于打印請求返回?cái)?shù)據(jù)信息
當(dāng)然你也可以根據(jù)自己的需求在這個(gè)模塊增加日志收集接口等等個(gè)人業(yè)務(wù)相關(guān)等需求
3.底層接口分離
- XXHNetwork跟底層AF交互的只有XHApiProxy一個(gè)類洲赵,同時(shí)只有一個(gè)方法(func callNetwork)這一個(gè)方法跟AF交互鸳惯,其他所有不同方式請求處理都是通過調(diào)用該方法,也就是以后如果想替換AFNetworking那么只需要更換這一個(gè)方法即可替換非常方便叠萍!
- 底層方法callNetwork還增加了一些基本的code處理芝发,對于一些明顯的錯(cuò)誤,例如token失效等俭令,這里直接放在最底層進(jìn)行處理這樣只需要處理一次即可后德。
- XHApiProxy提供供外界調(diào)用基本請求接口,同時(shí)還提供了請求控制接口抄腔,可以實(shí)現(xiàn)部分或者全部取消請求瓢湃。
4.上層接口
- block形式:XHNetworkBlockManager,只要實(shí)現(xiàn)相應(yīng)的成功赫蛇、失敗回調(diào)即可绵患,相關(guān)的錯(cuò)誤類型查看XHNetworkErrorType
- delegate形式:XHNetworkDelegateManager,這個(gè)可用于參數(shù)控制悟耘,數(shù)據(jù)返回檢測落蝙,只要實(shí)現(xiàn)相應(yīng)的代理即可實(shí)現(xiàn)相應(yīng)功能,詳細(xì)看注釋。
- XHNetworkConfigution:網(wǎng)絡(luò)配置,網(wǎng)絡(luò)基本配置在這里修改
- XHNetworkRequestConfig:請求配置筏勒,你可以根據(jù)業(yè)務(wù)需求初始化具體的配置移迫,你也可以不用管使用默認(rèn)的請求配置。
- XHURLResponse:返回?cái)?shù)據(jù)對象管行,不管成功還是失敗都會(huì)返回該對象厨埋,該對象包含了所有需要信息,AN返回的基本數(shù)據(jù)responseObject為(content)捐顷,error:為錯(cuò)誤信息荡陷。其他信息請查看注釋。
5.使用
- block使用:
XHNetworkBlockManager.shared.request(Method: XHNetworkRequestType.get, APIString: "", Parameters: params, SuccessBlock: { (successResponse) in
print("--------------方式一(block形式)請求數(shù)據(jù)成功----------")
self.testManager.loadData()
}) { (failureResponse) in
print("--------------方式一請求數(shù)據(jù)失敗----------")
}
- delegate使用:
1.創(chuàng)建一個(gè)繼承XHNetworkDelegateManager的業(yè)務(wù)請求中間管理類:XHNetworkTestManager迅涮。這個(gè)類專門來實(shí)現(xiàn)一些公用的業(yè)務(wù)代理XHAPIManagerValidator废赞、XHAPIManager等當(dāng)然你也可以不用,那么你的代理實(shí)現(xiàn)就必須在具體的業(yè)務(wù)中叮姑,這樣會(huì)增加業(yè)務(wù)代碼量唉地,所以不推薦。
2.調(diào)用
統(tǒng)一加載方法: loadData
加載更多方法:loadMoreData
//代理方式調(diào)用
self.testManager.loadData()
3.在具體業(yè)務(wù)中創(chuàng)建一個(gè)中間管理類的對象testManager
然后在業(yè)務(wù)接著完成剩余代理的實(shí)現(xiàn)
lazy var testManager:XHNetworkTestManager = {
let testManager = XHNetworkTestManager()
testManager.callBackDelagate = self
testManager.paramSourceDelegate = self
return testManager
}()
//參數(shù)代理传透,在這里傳送請求參數(shù)
func configeApiParams(Manager manager: XHNetworkDelegateManager) -> [String : Any]? {
let dic = [String : Any]()
return dic
}
//返回?cái)?shù)據(jù)代理
func requesApiSuccess(Manager manager: XHNetworkDelegateManager) {
print("--------------方式二(delegate形式)請求數(shù)據(jù)成功----------")
}
func requesApiFailure(Manager manager: XHNetworkDelegateManager) {
print("--------------方式二請求數(shù)據(jù)失敗----------")
}
源碼地址請點(diǎn)擊這里渣蜗,歡迎大神指導(dǎo),若考慮不周的地方旷祸,請大家多提意見!如果喜歡或者對您有幫助讼昆,希望能給個(gè)小星星托享,多謝!