本文是投稿文章
原文:swift網絡庫Alamofire源碼分析
本篇文章對於swift下的網絡庫alamofire的實現源碼進行分析下(基於版本3.1.1)
目錄結構
首先看下源碼結構,Source目錄下包含Alamofire.swift文件,主要是暴露一些常用的調用接口給我們,Core文件夾下是一些核心的功能實現文件,Feature是在Core的基礎上包裝成我們更常用,更方便使用的功能
如果你只想知道如何使用這個庫的話,基本上你只需要了解Alamofire.swift以及Core/ResponseSerialization.swift這兩個文件就可以了
通用工具函數或類
在進行整體分析前,先來說明下庫當中的一些通用的東西
Error.swift用於生成NSError錯誤對象, 包含一些常用網絡錯誤 如數據錯誤 服務器狀態錯誤,解析序列錯誤等
ParameterEncoding.swift 網絡請求時的HTTP的content-type 如application/x-www-form-urlencoded; charset=utf-8, application/json等
Result.swift 網絡返回結果, 一個枚舉, 包括Success和Failure, 其成功時會有數據, 失敗時會有錯誤信息, 用於後面的response
Alamofire.swift裡定義的兩個protocol,public protocol URLStringConvertible:方便NSURLRequest與String的轉換, 以及public protocol URLRequestConvertible
整體流程圖
上圖為alalmofire庫的網絡請求流程圖, 後面會根據此圖對一個網絡請求從開始到結束的整體流程進行分析.由於NSURLSession包含data, upload, download等幾種不同task, 本篇主要跟蹤data的流程, 其余兩個流程及原理都是相同的, 只是類型不同
根據使用文檔, 如果要發起一個GET請求, 服務器返回的為JSON類型數據, 只需要使用如下方法
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) .responseJSON { response in print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { print("JSON: \(JSON)") } }
來看看Alamofire.request方法做了什麼, 它調用了Manager類當中的request方法
public func request( method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil) -> Request { return Manager.sharedInstance.request( method, URLString, parameters: parameters, encoding: encoding, headers: headers ) }
Manager.swift
public class Manager: Manager類有個單例方法, 該方法的生成的NSURLSession的configuration為系統默認的NSURLSessionConfiguration.defaultSessionConfiguration(), 將session的delegate置為自己的成員變量delegate: SessionDelegate. 這個delegate既有session回調代理的作用,也有task回調代理調度分發的作用,它會根據不同的task類別分配給不同類別的delegate對象處理. 上一步中request的對應方法為
public func request( method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil) -> Request { let mutableURLRequest = URLRequest(method, URLString, headers: headers) let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 return request(encodedURLRequest) } public func request(URLRequest: URLRequestConvertible) -> Request { var dataTask: NSURLSessionDataTask! dispatch_sync(queue) { dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest) } let request = Request(session: session, task: dataTask) delegate[request.delegate.task] = request.delegate if startRequestsImmediately { request.resume() } return request }
這裡會依據URLRequest生成一個dataTask(防止多線程問題, 生成都在一個線程中進行), 同時由session與dataTask生成一個Request對象(這裡的request並不是我們常用的NSURLRequest,而是Alamofire中封裝的Request對象)返回, 此方法中的delegate[request.delegate.task] = request.delegate, 指定了這個dataTask的對應的delegate回調處理類, 下面會講到.
public final class SessionDelegate: 此類被聲明為final對象, 不准被繼承
public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate { private var subdelegates: [Int: Request.TaskDelegate] = [:] private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT) subscript(task: NSURLSessionTask) -> Request.TaskDelegate? { get { var subdelegate: Request.TaskDelegate? dispatch_sync(subdelegateQueue) { subdelegate = self.subdelegates[task.taskIdentifier] } return subdelegate } set { dispatch_barrier_async(subdelegateQueue) { self.subdelegates[task.taskIdentifier] = newValue } } }
可以看到它實現了session以及3種不同task的代理. 此類中實現了subscript的下標功能, 使之能更方便對不同的task根據不同task id調用不同的delegate處理方法.
實際上是通過subdelegate數組實現,裡面存儲的每個元素是一個字典, 這個字典中擁有key為taskIdentifier, value為對應的真正要處理delegate的類. 也是為了防止多線程問題使用了dispatch_barrier_async來確保線程安全。
為了使使用更加靈活,這個類中實現了類似攔截的機制, 使之可以自定義session的回調,進行自定義, 做為一個類庫, 肯定不能因為自己的一些特殊用處就在自己項目中對此類庫源碼隨意修改(當然你准備自己維護一套另當別論), 所以它自對每個delegate方法都給出自定義的實現, 我們如果有這方面的需求, 可以參考這種實現
public override func respondsToSelector(selector: Selector) -> Bool { switch selector { case "URLSession:didBecomeInvalidWithError:": return sessionDidBecomeInvalidWithError != nil case "URLSession:didReceiveChallenge:completionHandler:": return sessionDidReceiveChallenge != nil case "URLSessionDidFinishEventsForBackgroundURLSession:": return sessionDidFinishEventsForBackgroundURLSession != nil case "URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:": return taskWillPerformHTTPRedirection != nil case "URLSession:dataTask:didReceiveResponse:completionHandler:": return dataTaskDidReceiveResponse != nil default: return self.dynamicType.instancesRespondToSelector(selector) } }
比如我想對sessionDidReceiveChallenge自定義, 我只需要實現
Manager.sharedInstance.delegate.sessionDidReceiveChallenge = {(session, challenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential) in let disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling let credential = NSURLCredential(user: "user", password: "password", persistence: .None) return (disposition, credential) }
就可以完成對challenge的自定義
Request.swift
public class Request: 在初始化時根據不同task生成不同的delegate處理類, 每個類當中都有對應的回調函數
init(session: NSURLSession, task: NSURLSessionTask) { self.session = session switch task { case is NSURLSessionUploadTask: self.delegate = UploadTaskDelegate(task: task) case is NSURLSessionDataTask: self.delegate = DataTaskDelegate(task: task) case is NSURLSessionDownloadTask: self.delegate = DownloadTaskDelegate(task: task) default: self.delegate = TaskDelegate(task: task) } }
public class TaskDelegate 及其對應子類 DataTaskDelegate, UploadTaskDelegate, DownloadTaskDelegate:
這些類才是真正之前講的要處理task對應回調任務, 存儲data, error, progress等信息, 負責task的suspend, resume, cancel等基本操作
裡面的成員變量 public let queue: NSOperationQueue是很重要的一個元素, 後面的response的處理都是在它上面 被初始化後suspended被置為true,只有當task完成之後 (func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?)), 才被置為false,此時加在它內部的operation即response的解析化才會真正執行,解析後執行回調completionHandler
response.swift
public struct Response
public struct Response{ /// The URL request sent to the server. public let request: NSURLRequest? /// The server's response to the URL request. public let response: NSHTTPURLResponse? /// The data returned by the server. public let data: NSData? /// The result of response serialization. public let result: Result /** Initializes the `Response` instance with the specified URL request, URL response, server data and response serialization result. - parameter request: The URL request sent to the server. - parameter response: The server's response to the URL request. - parameter data: The data returned by the server. - parameter result: The result of response serialization. - returns: the new `Response` instance. */ public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result) { self.request = request self.response = response self.data = data self.result = result } }
ResponseSerialization.swift
public protocol ResponseSerializerType:序列化的協議, 定義了對response序列化的協議函數, 這裡有一個值得學習的是對typealias的應用,可參考typealias 和泛型接口
public struct ResponseSerializer
extension Request: 對Request的序列化的擴展實現, 比如我們一開始的responseJson函數, 通過調用JSONResponseSerializer返回一個序列化對象,此函數中用到了swift的do-catch 機制來獲取和處理異常, 最後統一調用了response函數, 這裡就是前面所講的在request.delegate的operationQueue中添加了一個執行序列化的operation, 完成後將封裝好的response對象通過block回調傳出。
public static func JSONResponseSerializer( options options: NSJSONReadingOptions = .AllowFragments) -> ResponseSerializer { return ResponseSerializer { _, response, data, error in guard error == nil else { return .Failure(error!) } if let response = response where response.statusCode == 204 { return .Success(NSNull()) } guard let validData = data where validData.length > 0 else { let failureReason = "JSON could not be serialized. Input data was nil or zero length." let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason) return .Failure(error) } do { let JSON = try NSJSONSerialization.JSONObjectWithData(validData, options: options) return .Success(JSON) } catch { return .Failure(error as NSError) } } } public func responseJSON( options options: NSJSONReadingOptions = .AllowFragments, completionHandler: Response-> Void) -> Self { return response( responseSerializer: Request.JSONResponseSerializer(options: options), completionHandler: completionHandler ) } public func response( queue queue: dispatch_queue_t? = nil, responseSerializer: T, completionHandler: Response-> Void) -> Self { delegate.queue.addOperationWithBlock { let result = responseSerializer.serializeResponse( self.request, self.response, self.delegate.data, self.delegate.error ) dispatch_async(queue ?? dispatch_get_main_queue()) { let response = Response( request: self.request, response: self.response, data: self.delegate.data, result: result ) completionHandler(response) } } return self }
到這裡就完成了一次請求流程,對於upload及download流程都是一樣的
總結:
Alamofire是很好用的一個網絡庫, 使用起來很方便,不過如果項目有一定規模及復雜度,建議還是在項目中按照自己的需求進行一定的封裝
因為是基於NSURLSession實現的網絡請求, 如果你有多個並發請求, 比如下載多個文件,直接發出10個請求, 但你只想支持最大兩個並發, NSURLSession的timeoutIntervalForRequest以及timeoutIntervalForResource並不能滿足你的需求, 你必須自己使用operationqueue 實現。
參考資料:
Swift Generics Tutorial :這篇文章對泛型講解的很好,不過示例項目是不是swift2.0的, 只要稍微改下便可運行
typealias 和泛型接口