iOS開發(fā)面試題及其答案(四)網(wǎng)絡(luò)層

一、基礎(chǔ)概念與核心API

1. iOS中常用的網(wǎng)絡(luò)請求框架有哪些?各自的優(yōu)缺點(diǎn)是什么?

答案:

? URLSession(iOS 7+):

優(yōu)點(diǎn):基于任務(wù)(Task)設(shè)計(jì),支持?jǐn)?shù)據(jù)、上傳/下載、WebSocket任務(wù);支持?jǐn)帱c(diǎn)續(xù)傳、后臺(tái)任務(wù)、網(wǎng)絡(luò)質(zhì)量監(jiān)控;集成NSURLSessionConfiguration靈活配置(如超時(shí)、緩存、代理);支持Codable直接解碼響應(yīng)數(shù)據(jù)。

缺點(diǎn):相比AFNetworking需手動(dòng)處理更多細(xì)節(jié)(如請求/響應(yīng)序列化)。

? AFNetworking(第三方):

優(yōu)點(diǎn):封裝度高,簡化請求/響應(yīng)處理(自動(dòng)序列化JSON/XML)、支持證書固定、請求重試、進(jìn)度監(jiān)聽;社區(qū)活躍,兼容性強(qiáng)。

缺點(diǎn):基于NSURLSession二次封裝,增加包體積;長期未維護(hù)(最新版本2020年),可能存在iOS新版本適配問題。

? Alamofire(第三方):

優(yōu)點(diǎn):Swift原生,鏈?zhǔn)紸PI更簡潔;支持Combine/RxSwift響應(yīng)式編程;內(nèi)置參數(shù)編碼/解碼、證書固定、HTTP/2支持。

缺點(diǎn):依賴Swift版本,OC項(xiàng)目需橋接。

2. URLSession的三種任務(wù)(Data Task、Upload Task、Download Task)的區(qū)別是什么?

答案:

? Data Task:用于獲取數(shù)據(jù)(如JSON/圖片),響應(yīng)數(shù)據(jù)通過閉包返回內(nèi)存,適合小數(shù)據(jù)量。

? Upload Task:用于上傳文件/數(shù)據(jù),支持Data、FileURL、InputStream作為上傳體,可監(jiān)聽上傳進(jìn)度。

? Download Task:用于下載文件,直接將數(shù)據(jù)寫入文件系統(tǒng)(臨時(shí)目錄),支持?jǐn)帱c(diǎn)續(xù)傳(通過resumeData),可監(jiān)聽下載進(jìn)度。

3. HTTP和HTTPS的區(qū)別是什么?HTTPS如何保證數(shù)據(jù)安全?

答案:

? 區(qū)別:

端口:HTTP默認(rèn)80,HTTPS默認(rèn)443。

安全性:HTTP明文傳輸,HTTPS通過TLS/SSL加密。

性能:HTTPS需握手過程,耗時(shí)略高(可通過HTTP/2和TLS 1.3優(yōu)化)。

? 安全機(jī)制:

對稱加密:客戶端和服務(wù)器協(xié)商生成密鑰,加密傳輸數(shù)據(jù)(如AES)。

非對稱加密:服務(wù)器用私鑰加密證書,客戶端用公鑰驗(yàn)證(解決對稱密鑰傳輸安全問題)。

數(shù)字證書:由CA機(jī)構(gòu)簽發(fā),包含服務(wù)器公鑰、域名、有效期等,防止中間人攻擊。

4. RESTful API的設(shè)計(jì)原則有哪些?iOS開發(fā)中如何實(shí)現(xiàn)RESTful請求?

答案:

? 設(shè)計(jì)原則:

資源定位:URL表示資源(如GET /users/123獲取用戶信息)。

動(dòng)作語義:使用HTTP方法(GET/POST/PUT/DELETE)表示操作。

無狀態(tài):每個(gè)請求包含完整上下文,不依賴服務(wù)器會(huì)話。

響應(yīng)格式:使用JSON/XML作為數(shù)據(jù)載體,狀態(tài)碼(2xx成功,4xx客戶端錯(cuò)誤,5xx服務(wù)器錯(cuò)誤)。

? iOS實(shí)現(xiàn):

用URLSession構(gòu)建請求,設(shè)置HTTPMethod為對應(yīng)方法。

處理響應(yīng)時(shí)解析狀態(tài)碼,結(jié)合URLResponse判斷成功/失敗。

二、網(wǎng)絡(luò)層設(shè)計(jì)與最佳實(shí)踐

5. 如何設(shè)計(jì)一個(gè)可復(fù)用的iOS網(wǎng)絡(luò)層?需要考慮哪些方面?

答案:

? 分層設(shè)計(jì):

基礎(chǔ)層:封裝URLSession,處理公共參數(shù)(Token、User-Agent)、請求/響應(yīng)序列化(JSON轉(zhuǎn)Model)、錯(cuò)誤統(tǒng)一處理。

業(yè)務(wù)層:定義API枚舉(如API.User.login),組裝請求參數(shù),處理特定業(yè)務(wù)邏輯(如Token刷新)。

管理層:單例管理網(wǎng)絡(luò)配置(超時(shí)、緩存策略)、請求隊(duì)列(控制并發(fā)數(shù))、網(wǎng)絡(luò)狀態(tài)監(jiān)聽(通過Reachability)。

? 核心模塊:

公共參數(shù)自動(dòng)附加(如在請求頭添加Authorization)。

統(tǒng)一錯(cuò)誤處理(區(qū)分網(wǎng)絡(luò)錯(cuò)誤、服務(wù)器錯(cuò)誤、解析錯(cuò)誤)。

緩存策略配置(如針對GET請求啟用NSURLCache)。

6. 如何處理網(wǎng)絡(luò)請求中的公共參數(shù)(如Token、版本號(hào))?

答案:

? 請求攔截器:在發(fā)送請求前,通過URLSessionConfiguration的URLSessionTask的willSendRequest代理方法,或自定義網(wǎng)絡(luò)層的攔截器,動(dòng)態(tài)添加請求頭或URL參數(shù)。

? 示例代碼(URLSession擴(kuò)展):

extension URLSession {?

? ? func dataTask(with request: URLRequest, token: String) -> URLSessionDataTask {?

? ? ? ? var req = request?

? ? ? ? req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")?

? ? ? ? req.setValue("iOS/1.0", forHTTPHeaderField: "App-Version")?

? ? ? ? return dataTask(with: req)?

? ? }?

}?

7. 如何實(shí)現(xiàn)網(wǎng)絡(luò)請求的重試機(jī)制?有哪些策略?

答案:

? 策略:

固定次數(shù)重試:如失敗后重試3次(適用于臨時(shí)網(wǎng)絡(luò)波動(dòng))。

指數(shù)退避(Exponential Backoff):重試間隔按指數(shù)增長(如1s→2s→4s→…),避免服務(wù)器被大量重試請求壓垮。

條件重試:僅針對特定錯(cuò)誤碼(如500/503服務(wù)器錯(cuò)誤,或網(wǎng)絡(luò)連接恢復(fù)后的錯(cuò)誤)。

? 實(shí)現(xiàn):

在網(wǎng)絡(luò)層的錯(cuò)誤處理閉包中,判斷是否需要重試,遞歸或循環(huán)發(fā)送請求,記錄重試次數(shù)。

結(jié)合URLSessionTask的cancel()取消舊任務(wù),避免資源泄漏。

8. iOS中如何處理HTTPS的證書驗(yàn)證?什么是證書固定(Certificate Pinning)?

答案:

? 系統(tǒng)默認(rèn)驗(yàn)證:iOS自動(dòng)驗(yàn)證服務(wù)器證書是否由可信CA簽發(fā),域名是否匹配。

? 證書固定(強(qiáng)驗(yàn)證):

公鑰固定:客戶端存儲(chǔ)服務(wù)器公鑰的哈希值,驗(yàn)證響應(yīng)證書的公鑰是否匹配(比證書固定更靈活,允許CA證書更換)。

證書固定:直接存儲(chǔ)服務(wù)器證書的哈希值,驗(yàn)證響應(yīng)證書是否完全一致(安全性更高,但證書更新時(shí)需升級(jí)App)。

? 實(shí)現(xiàn)(URLSession配置):

let config = URLSessionConfiguration.default?

config.urlCache = nil // 關(guān)閉緩存驗(yàn)證,避免使用舊證書?

config.waitsForConnectivity = true // 等待網(wǎng)絡(luò)連接?

let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)?

// 實(shí)現(xiàn)URLSessionDelegate的didReceive challenge方法,手動(dòng)驗(yàn)證證書?

三、性能優(yōu)化與緩存策略

9. iOS網(wǎng)絡(luò)請求的緩存策略有哪些?如何配置NSURLCache?

答案:

? HTTP緩存策略(通過URLRequest的cachePolicy):

.useProtocolCachePolicy(默認(rèn)):遵循HTTP緩存頭(如Cache-Control)。

.reloadIgnoringLocalCacheData:忽略本地緩存,直接請求服務(wù)器。

.returnCacheDataElseLoad:優(yōu)先使用緩存,無緩存時(shí)請求服務(wù)器(適用于離線場景)。

? NSURLCache配置:

let cacheSize = 50 * 1024 * 1024 // 50MB內(nèi)存緩存?

let diskCache = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!).appendingPathComponent("networkCache")?

let cache = NSURLCache(memoryCapacity: cacheSize, diskCapacity: cacheSize * 2, diskPath: diskCache.path)?

URLSessionConfiguration.default.urlCache = cache?

結(jié)合HTTP響應(yīng)頭(如ETag、Last-Modified)實(shí)現(xiàn)協(xié)商緩存(304 Not Modified)。

10. 如何優(yōu)化網(wǎng)絡(luò)請求的延遲和流量消耗?

答案:

? 減少請求次數(shù):

合并多個(gè)API為一個(gè)批量接口(如GraphQL)。

使用HTTP/2(支持多路復(fù)用,減少TCP握手次數(shù))。

? 壓縮數(shù)據(jù):

服務(wù)器返回GZip/Deflate壓縮的JSON數(shù)據(jù)(客戶端自動(dòng)解壓縮,需設(shè)置請求頭Accept-Encoding: gzip, deflate)。

? 緩存策略:

對不常變更的數(shù)據(jù)啟用本地緩存(NSURLCache)和HTTP協(xié)商緩存。

? 網(wǎng)絡(luò)質(zhì)量感知:

通過NWPathMonitor檢測網(wǎng)絡(luò)類型(Wi-Fi/4G/5G),動(dòng)態(tài)調(diào)整請求策略(如4G下降低圖片分辨率)。

11. 大文件下載如何實(shí)現(xiàn)斷點(diǎn)續(xù)傳?需要注意哪些問題?

答案:

? 實(shí)現(xiàn)步驟:

1. 首次下載時(shí),通過Download Task獲取文件總大小,記錄臨時(shí)文件路徑和resumeData。

2. 下載中斷時(shí),保存resumeData到本地(如沙盒Documents目錄)。

3. 恢復(fù)下載時(shí),用resumeData創(chuàng)建新的Download Task,服務(wù)器通過Range請求頭(如bytes=1000-)續(xù)傳。

? 注意點(diǎn):

服務(wù)器需支持206 Partial Content狀態(tài)碼。

處理文件合并(如多個(gè)分片下載時(shí)需按順序合并)。

避免resumeData過大(每個(gè)任務(wù)約1KB,過多任務(wù)可能占用存儲(chǔ))。

12. 如何監(jiān)控網(wǎng)絡(luò)請求的性能(耗時(shí)、流量、錯(cuò)誤率)?

答案:

? URLSession Metrics:

task.resume()?

task.metrics { metrics in?

? ? print("DNS耗時(shí):\(metrics.dnsTime)")?

? ? print("TCP連接耗時(shí):\(metrics.connectionTime)")?

? ? print("下載字節(jié)數(shù):\(metrics.bytesReceived)")?

}?

? 自定義監(jiān)控:

在請求開始和結(jié)束時(shí)記錄時(shí)間戳,計(jì)算耗時(shí)。

統(tǒng)計(jì)錯(cuò)誤碼(如4xx/5xx)出現(xiàn)頻率,上報(bào)服務(wù)器。

? 工具:

用Instruments的Network模板抓包分析。

集成Firebase Performance、APM工具(如New Relic)進(jìn)行線上監(jiān)控。

四、網(wǎng)絡(luò)穩(wěn)定性與異常處理

13. 如何處理網(wǎng)絡(luò)請求中的Token過期問題?

答案:

? 自動(dòng)刷新Token:

1. 當(dāng)服務(wù)器返回401/403狀態(tài)碼時(shí),觸發(fā)Token刷新邏輯。

2. 發(fā)送刷新Token的請求(需攜帶Refresh Token),獲取新的Access Token。

3. 重試原請求,附帶新Token。

? 注意點(diǎn):

保證刷新Token的請求是原子性的(同一時(shí)間僅允許一個(gè)刷新請求)。

刷新失敗時(shí),引導(dǎo)用戶重新登錄。

14. 移動(dòng)端網(wǎng)絡(luò)不穩(wěn)定時(shí),如何保證數(shù)據(jù)的可靠性(如弱網(wǎng)、延遲高)?

答案:

? 優(yōu)化請求:

減少請求體大?。ㄈ缡褂肞rotocol Buffers替代JSON)。

啟用TCP快速打開(TFO,需服務(wù)器支持)。

? 錯(cuò)誤重試:

結(jié)合指數(shù)退避策略,避免頻繁重試加劇網(wǎng)絡(luò)擁塞。

? 本地持久化:

對重要操作(如提交表單)先緩存到本地?cái)?shù)據(jù)庫,網(wǎng)絡(luò)恢復(fù)后自動(dòng)重試(需處理冪等性,如請求附帶唯一ID)。

15. 如何處理JSON數(shù)據(jù)解析時(shí)的異常(如字段缺失、類型不匹配)?

答案:

? 使用Codable的可選類型和默認(rèn)值:

struct User: Codable {?

? ? let id: Int?

? ? let name: String? = "Guest" // 可選字段,提供默認(rèn)值?

? ? let age: Int? // 缺失時(shí)為nil?

}?

? 自定義Decodable實(shí)現(xiàn):

處理復(fù)雜邏輯(如時(shí)間戳轉(zhuǎn)Date、枚舉映射):

enum Gender: String, Codable {?

? ? case male, female?

? ? init(from decoder: Decoder) throws {?

? ? ? ? let rawValue = try decoder.singleValueContainer().decode(String.self)?

? ? ? ? self = Gender(rawValue: rawValue.lowercased()) ?? .male?

? ? }?

}?

? 防御性解析:

在網(wǎng)絡(luò)層統(tǒng)一處理解析錯(cuò)誤,返回友好的業(yè)務(wù)錯(cuò)誤(而非崩潰)。

五、進(jìn)階原理與問題排查

16. TCP三次握手和四次揮手的過程是什么?iOS中如何影響網(wǎng)絡(luò)請求?

答案:

? 三次握手:

1. 客戶端發(fā)送SYN包,請求建立連接。

2. 服務(wù)器返回SYN+ACK包,確認(rèn)請求。

3. 客戶端返回ACK包,連接建立(耗時(shí)約一個(gè)RTT)。

? 四次揮手:

1. 客戶端發(fā)送FIN包,請求關(guān)閉連接。

2. 服務(wù)器返回ACK包,確認(rèn)接收FIN。

3. 服務(wù)器發(fā)送FIN包,通知客戶端關(guān)閉。

4. 客戶端返回ACK包,連接關(guān)閉(耗時(shí)約兩個(gè)RTT)。

? 對iOS的影響:

短連接(每次請求新建連接)會(huì)增加三次握手耗時(shí),建議使用長連接(HTTP/1.1默認(rèn)keep-alive,HTTP/2強(qiáng)制長連接)。

后臺(tái)任務(wù)需注意連接關(guān)閉時(shí)機(jī),避免頻繁重建連接。

17. iOS中如何檢測網(wǎng)絡(luò)連接狀態(tài)?有哪些注意事項(xiàng)?

答案:

? 方法:

舊方法(已廢棄):Reachability類(基于SCNetworkReachability),監(jiān)聽網(wǎng)絡(luò)是否可達(dá)。

新方法(iOS 12+):NWPathMonitor(Network框架),檢測更細(xì)粒度的網(wǎng)絡(luò)狀態(tài)(如Wi-Fi/蜂窩/無網(wǎng)絡(luò),是否受限連接):

let monitor = NWPathMonitor()?

monitor.pathUpdateHandler = { path in?

? ? if path.status == .ready {?

? ? ? ? if path.usesInterfaceType(.wifi) {?

? ? ? ? ? ? // Wi-Fi連接?

? ? ? ? } else if path.usesInterfaceType(.cellular) {?

? ? ? ? ? ? // 蜂窩網(wǎng)絡(luò)?

? ? ? ? }?

? ? } else {?

? ? ? ? // 無網(wǎng)絡(luò)?

? ? }?

}?

let queue = DispatchQueue(label: "network.monitor")?

monitor.start(queue: queue)?

? 注意點(diǎn):

避免頻繁檢查網(wǎng)絡(luò)狀態(tài),用回調(diào)代替輪詢。

即使網(wǎng)絡(luò)可達(dá),請求仍可能失?。ㄈ绶?wù)器宕機(jī)),需在請求層統(tǒng)一處理錯(cuò)誤。

18. HTTPS握手過程中,客戶端和服務(wù)器交換了哪些信息?

答案:

1. 客戶端發(fā)送:

支持的TLS版本、加密算法列表(Cipher Suite)、隨機(jī)數(shù)(Client Random)。

2. 服務(wù)器響應(yīng):

選擇的TLS版本、加密算法、服務(wù)器證書(含公鑰)、隨機(jī)數(shù)(Server Random)。

3. 客戶端驗(yàn)證證書:

檢查證書有效性(CA簽名、域名、有效期),生成預(yù)主密鑰(Pre-Master Secret),用服務(wù)器公鑰加密后發(fā)送。

4. 雙方計(jì)算主密鑰:

通過Client Random、Server Random、Pre-Master Secret生成相同的主密鑰(Master Secret),用于后續(xù)對稱加密。

5. 客戶端發(fā)送加密握手消息:

通知服務(wù)器后續(xù)數(shù)據(jù)加密傳輸。

6. 服務(wù)器發(fā)送加密握手消息:

完成握手,開始傳輸加密數(shù)據(jù)。

19. 如何排查iOS網(wǎng)絡(luò)請求失敗的問題?有哪些常用工具?

答案:

? 排查步驟:

1. 確認(rèn)網(wǎng)絡(luò)連接狀態(tài)(用NWPathMonitor或系統(tǒng)設(shè)置)。

2. 檢查URL是否正確(編碼問題,如空格未轉(zhuǎn)義為%20)。

3. 打印請求頭和響應(yīng)頭,查看狀態(tài)碼(如404資源不存在,500服務(wù)器內(nèi)部錯(cuò)誤)。

4. 分析NSURLSession的錯(cuò)誤信息(NSError的code,如-1009表示網(wǎng)絡(luò)斷開)。

? 工具:

Charles/Whistle:抓包分析請求/響應(yīng)數(shù)據(jù),模擬弱網(wǎng)環(huán)境。

Wireshark:底層網(wǎng)絡(luò)包分析(需配置iOS設(shè)備代理)。

Xcode控制臺(tái):打印URLSession的description和metrics。

Instruments的Network模板:監(jiān)控網(wǎng)絡(luò)流量、連接時(shí)間、SSL耗時(shí)。

20. iOS中如何處理HTTP/2的優(yōu)勢?需要注意哪些兼容性問題?

答案:

? 優(yōu)勢:

多路復(fù)用:一個(gè)TCP連接并發(fā)處理多個(gè)請求,避免隊(duì)頭阻塞。

頭部壓縮:HPACK算法壓縮HTTP頭,減少流量。

服務(wù)器推送:服務(wù)器主動(dòng)推送資源(如HTML引用的CSS/JS),減少客戶端請求。

? 兼容性:

iOS 9+默認(rèn)支持HTTP/2(需服務(wù)器啟用ALPN協(xié)議)。

需確認(rèn)服務(wù)器配置(TLS版本≥1.2,支持ALPN)。

混合內(nèi)容(HTTP+HTTPS)可能導(dǎo)致問題,建議全站HTTPS。

? 配置:

let config = URLSessionConfiguration.default?

config.http20Enabled = true // 手動(dòng)啟用(iOS默認(rèn)自動(dòng)啟用HTTPS下的HTTP/2)?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容