版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.05.19 |
前言
前面寫了那么多篇主要著眼于局部問題的解決败砂,包括特定功能的實現(xiàn)腕巡、通用工具類的封裝造虎、視頻和語音多媒體的底層和實現(xiàn)以及動畫酷炫的實現(xiàn)方式等等声邦。接下來這幾篇我們就一起看一下關(guān)于iOS系統(tǒng)架構(gòu)以及獨(dú)立做一個APP的架構(gòu)設(shè)計的相關(guān)問題虫几。感興趣的可以看上面幾篇豹爹。
1. 架構(gòu)之路 (一) —— iOS原生系統(tǒng)架構(gòu)(一)
2. 架構(gòu)之路 (二) —— APP架構(gòu)分析(一)
回顧
上一篇主要講述了一個APP的架構(gòu)分析和設(shè)計滑蚯,這一篇我們就說一下APP架構(gòu)中的網(wǎng)絡(luò)層俺附。
網(wǎng)絡(luò)層在APP中的地位
網(wǎng)絡(luò)層就是APP從服務(wù)器獲取信息的一個媒介和方式肥卡,沒有網(wǎng)絡(luò)層,可以說APP就是一潭死水事镣,有沒有沒有網(wǎng)絡(luò)層的APP存在呢步鉴?有,但是很少,比如一些不需要和服務(wù)器交互的APP就不需要網(wǎng)絡(luò)層氛琢,比如你做一個簡單的相機(jī)喊递,帶有各種濾鏡處理,也不需要注冊登錄等邏輯阳似,照完像就保存到本地骚勘,那么這個APP就不需要網(wǎng)絡(luò)層,但是極大多數(shù)APP都是需要網(wǎng)絡(luò)層的撮奏,有了網(wǎng)絡(luò)層你的APP才會“動起來”俏讹。
casa大牛對網(wǎng)絡(luò)層搭建的想法
關(guān)于網(wǎng)絡(luò)層的搭建,有很多大神的博客上都說的很多比如畜吊,Casa Taloyum的博客藐石。他主要說了三個問題,分別如下所示:
1. 網(wǎng)絡(luò)層跟業(yè)務(wù)對接部分的設(shè)計
網(wǎng)絡(luò)層跟業(yè)務(wù)層對接部分設(shè)計的好壞定拟,會直接影響到業(yè)務(wù)工程師實現(xiàn)功能時的心情。主要是圍繞下面三個問題進(jìn)行闡述的逗嫡。
使用哪種交互模式來跟業(yè)務(wù)層做對接
這里其實有兩個問題:
- 以什么方式將數(shù)據(jù)交付給業(yè)務(wù)層青自?
這里casa
大神建議使用Delegate為主,Notification為輔驱证,不建議使用block延窜,因為block容易因為循環(huán)引用造成對象無法釋放,在網(wǎng)絡(luò)回調(diào)中使用block抹锄,是block導(dǎo)致對象生命周期被延長的其中一個場合逆瑞,當(dāng)ViewController從window中卸下時,如果尚有請求帶著block在外面飛伙单,然后block里面引用了ViewController(這種場合非常常見)获高,那么ViewController是不能被及時回收的,即便你已經(jīng)取消了請求吻育,那也還是必須得等到請求著陸之后才能被回收念秧。然而使用delegate就不會有這樣的問題,delegate是弱引用布疼,哪怕請求仍然在外面飛摊趾,ViewController還是能夠及時被回收的,回收之后指針自動被置為了nil游两,無傷大雅砾层。
casa
建議使用Notification
使用情景在于網(wǎng)絡(luò)層網(wǎng)絡(luò)狀況變化的情況,例如2G到3G等方式的變化贱案。
- 交付什么樣的數(shù)據(jù)給業(yè)務(wù)層肛炮?
網(wǎng)絡(luò)層數(shù)據(jù)交付這部分時,casa
添加了reformer
(名字而已,叫什么都好)這個對象用于封裝數(shù)據(jù)轉(zhuǎn)化的邏輯铸董,這個對象是一個獨(dú)立對象祟印,事實上,它是作為Adaptor
模式存在的粟害。我們可以這么理解:想象一下我們洗澡時候使用的蓮蓬頭蕴忆,水管里出來的水是API下發(fā)的原始數(shù)據(jù)。reformer就是蓮蓬頭上的不同水流擋板悲幅,需要什么模式套鹅,就撥到什么模式。
是否有必要將API返回的數(shù)據(jù)封裝成對象然后再交付給業(yè)務(wù)層
作者的意見是需要將API返回的數(shù)據(jù)封裝成對象汰具,在交給業(yè)務(wù)層的卓鹿。
使用集約化調(diào)用方式還是離散型調(diào)用方式去調(diào)用API
集約型API調(diào)用其實就是所有API的調(diào)用只有一個類,然后這個類接收API名字留荔,API參數(shù)吟孙,以及回調(diào)著陸點(可以是target-action,或者block聚蝶,或者delegate等各種模式的著陸點)作為參數(shù)杰妓。然后執(zhí)行類似startRequest這樣的方法,它就會去根據(jù)這些參數(shù)起飛去調(diào)用API了碘勉,然后獲得API數(shù)據(jù)之后再根據(jù)指定的著陸點去著陸巷挥。
集約型API調(diào)用方式:
[APIRequest startRequestWithApiName:@"itemList.v1" params:params success:@selector(success:) fail:@selector(fail:) target:self];
離散型API調(diào)用是這樣的,一個API對應(yīng)于一個APIManager验靡,然后這個APIManager只需要提供參數(shù)就能起飛倍宾,API名字、著陸方式都已經(jīng)集成入APIManager中胜嗓。比如這樣:
離散型API調(diào)用方式:
@property (nonatomic, strong) ItemListAPIManager *itemListAPIManager;
// getter
- (ItemListAPIManager *)itemListAPIManager
{
if (_itemListAPIManager == nil) {
_itemListAPIManager = [[ItemListAPIManager alloc] init];
_itemListAPIManager.delegate = self;
}
return _itemListAPIManager;
}
// 使用的時候就這么寫:
[self.itemListAPIManager loadDataWithParams:params];
關(guān)于集約型的API調(diào)用和離散型的API調(diào)用高职,casa
傾向于這樣:對外提供一個BaseAPIManager
來給業(yè)務(wù)方做派生,在BaseManager
里面采用集約化的手段組裝請求辞州,放飛請求初厚,然而業(yè)務(wù)方調(diào)用API的時候,則是以離散的API調(diào)用方式來調(diào)用孙技。如果你的App只提供了集約化的方式产禾,而沒有離散方式的通道,那么我建議你再封裝一層牵啦,便于業(yè)務(wù)方使用離散的API調(diào)用方式來放飛請求亚情。
2. 網(wǎng)絡(luò)層的安全機(jī)制實現(xiàn)
- 判斷API的調(diào)用請求是來自于經(jīng)過授權(quán)的APP
- 保證傳輸數(shù)據(jù)的安全
- 建議使用HTTPS
3. 網(wǎng)絡(luò)層的優(yōu)化方案
網(wǎng)絡(luò)層的優(yōu)化手段主要從以下三方面考慮:
針對鏈接建立環(huán)節(jié)的優(yōu)化
- 使用緩存手段減少請求的發(fā)起次數(shù)
- 使用策略來減少請求的發(fā)起次數(shù)
針對鏈接傳輸數(shù)據(jù)量的優(yōu)化
傳輸?shù)臄?shù)據(jù)少了,那么自然速度就上去了哈雏。這里沒什么花樣可以講的楞件,就是壓縮唄衫生。
針對鏈接復(fù)用的優(yōu)化
建立鏈接本身是屬于比較消耗資源的操作,耗電耗時土浸。SPDY自帶鏈接復(fù)用以及數(shù)據(jù)壓縮的功能罪针,所以服務(wù)端支持SPDY的時候,App直接掛SPDY就可以了黄伊。如果服務(wù)端不支持SPDY泪酱,也可以使用PipeLine,蘋果原生自帶這個功能还最。
網(wǎng)絡(luò)層規(guī)范
這里另外一個牛人寫的也不錯墓阀,先把地址貼出來 —— iOS網(wǎng)絡(luò)層架構(gòu)設(shè)計分享
1. 網(wǎng)絡(luò)層接口規(guī)范
接口數(shù)據(jù)返回結(jié)構(gòu)
返回的是兩層三部分結(jié)構(gòu)code、msg拓轻、data
"code": 0,
"msg": "",
"data": {
"upload_log": true,
"has_update": false,
"admin_id": "529ecfd64"
}
-
code
:錯誤碼斯撮,這個用來定義各種錯誤原因,方便客戶端進(jìn)行各種處理扶叉。 -
msg
:接口文案提示勿锅,包括錯誤提示,用來直接顯示給用戶枣氧。 -
data
:需要返回的數(shù)據(jù)溢十,可以是字典,可以是數(shù)組作瞄。
關(guān)于請求方式
一般都提供標(biāo)準(zhǔn)的GET/POST請求方式。
2. 多服務(wù)器多環(huán)境設(shè)置
為什么要多服務(wù)器環(huán)境危纫?標(biāo)準(zhǔn)的APP是有4個環(huán)境的宗挥,開發(fā)、測試种蝶、預(yù)發(fā)契耿、正式,特別是服務(wù)器的代碼螃征,不能說所有的代碼更改都在正式環(huán)境下搪桂。
3. 網(wǎng)絡(luò)請求的取消
當(dāng)一個頁面的請求正在天上飛的時候,用戶等了好久不耐煩了盯滚,小手點了個back踢械,然后ViewController
被pop被回收。此時請求的著陸點就沒了魄藕。這是很危險的情況内列,著陸點要是沒了,就很容易crash的背率。
一般是按照下面方式進(jìn)行處理:使用了runtime话瞧,給ViewController添加一個字典嫩与,來保存requestID和BaseDataEngine
,這樣對于ViewController來說就不是必須要寫變量來持有BaseDataEngine
了交排。
在發(fā)起請求的時候進(jìn)行綁定
[control.networkingAutoCancelRequests setEngine:self requestID:self.requestID];
在請求完成的時候進(jìn)行刪除
[weakControl.networkingAutoCancelRequests removeEngineWithRequestID:engine.requestID];
我自己關(guān)于網(wǎng)絡(luò)層架構(gòu)的看法
現(xiàn)在絕大多數(shù)APP都不會自己封裝NSURLSession進(jìn)行網(wǎng)絡(luò)下載划滋,基本都是基于AFNetworking的二次開發(fā)的。因此埃篓,很多關(guān)于網(wǎng)絡(luò)的問題处坪,轉(zhuǎn)化為了對AFNetworking進(jìn)行二次封裝的優(yōu)劣的問題。我待過的公司都许,我見過封裝后用delegate回調(diào)數(shù)據(jù)的稻薇,也見過用block回調(diào)數(shù)據(jù)的,他們各有優(yōu)劣胶征。用deleagate回調(diào)數(shù)據(jù)的塞椎,可以說很麻煩,代碼很分散睛低,你要在請求的VC里面設(shè)置delegate案狠,還有實現(xiàn)代理方法,優(yōu)點就是弱引用钱雷,不會引起循環(huán)引用造成VC釋放了骂铁,請求還在飛的情況。block代碼就比較集中了罩抗,處理起來很簡明拉庵,但是容易引起retain,這個是需要注意的問題套蒂。
對于
AFNetworking
的二次封裝钞支,建議抽象出一個BaseService,然后業(yè)務(wù)方根據(jù)不同host和需求應(yīng)用場景操刀,分離出自己的service子類烁挟,然后請求和鏈接網(wǎng)絡(luò)層。關(guān)于網(wǎng)絡(luò)層回來的數(shù)據(jù)骨坑,我建議也是采用
reformer
的形式比較好撼嗓,根據(jù)不同的需求可以直接返回不同的數(shù)據(jù),只需更改淋浴的出水口的蓮蓬頭就可以出不同形式的水欢唾。
參考文章
1. iOS應(yīng)用架構(gòu)談 網(wǎng)絡(luò)層設(shè)計方案
2. iOS網(wǎng)絡(luò)層架構(gòu)設(shè)計分享
3. iOS網(wǎng)絡(luò)架構(gòu)文檔整理
4. iOS網(wǎng)絡(luò)架構(gòu)文檔整理