Alamofire 框架淺析


 

下面是 Github 主頁上對 Alamofire 的描述

Elegant HTTP Networking in Swift

為什么這次我選擇閱讀 Alamofire 的源代碼而不是 AFNetworking 呢, 其實有兩點原因.

  1. AFNetworking 作為一個有着很多年的歷史的框架, 它雖然有着強大的社區, 不過因為時間太久了, 可能有一些歷史上的包袱. 而 Alamofire 是在 Swift 誕生之后才開始出現的, 到現在為止也並沒有多長時間, 它的源代碼都是新鮮的.
  2. 由於最近在寫 Swift 的項目, 所以沒有選擇 AFNetworking.

在閱讀 Alamofire 的源代碼之前, 我先粗略的查看了一下 Alamofire 實現的代碼行數:

$ find Source -name "*.swift" | xargs cat |wc -l
> 3363

也就是說 Alamofire 在包含注釋以及空行的情況下, 只使用了 3000 多行代碼就實現了一個用於處理 HTTP 請求的框架.

所以它描述中的 Elegant 也可以說是名副其實.

目錄結構

首先, 我們來看一下 Alamofire 中的目錄結構, 來了解一下它是如何組織各個文件的.

- Source
    - Alamore.swift
    - Core
        - Manager.swift
        - ParameterEncoding.swift
        - Request.swift
    - Features
        - Download.swift
        - MultipartFromData.swift
        - ResponseSeriallization.swift
        - Upload.swift
        - Validation.swift

框架中最核心並且我們最值得關注的就是 Alamore.swift Manager.swift 和 Request.swift 這三個文件. 也是在這篇 post 中主要介紹的三個文件.

Alamofire

在 Alamofire 中並沒有找到 Alamofire 這個類, 相反這僅僅是一個命名空間, 在 Alamofire.swift 這個文件中不存在 class Alamofire 這種關鍵字, 這只是為了使得方法名更簡潔的一種手段.

我們在使用 Alamofire 時, 往往都會采用這種方式:

Alamofire.request(.GET, "http://httpbin.org/get") 

有了 Alamofire 作為命名空間, 就不用擔心 request 方法與其他同名方法的沖突了.

在 Alamofire.swift 文件中為我們提供了三類方法:

  • request
  • upload
  • download

這三種方法都是通過調用 Manager 對應的操作來完成請求, 上傳和下載的操作, 並返回一個 Request 的實例.

下面是 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) } 

這也就是 Alamofire.request(.GET, "http://httpbin.org/get") 所調用的方法. 而這個方法實際上就是通過這些參數調用 Manager 的具體方法, 我們所使用的 request 也好 download 也好, 都是對 Manager 方法的一個包裝罷了.

Manager

Alamofire 中的幾乎所有操作都是通過 Manager 來控制, 而 Manager 也可以說是 Alamofire 的核心部分, 它負責與 Request 交互完成網絡操作:

Responsible for creating and managing Request objects, as well as their underlying NSURLSession.

Manager.sharedInstance

Manager 在 Alamofire 中有着極其重要的地位, 而在 Manager 方法的設計中, 一般也使用 sharedInstance 來獲取 Manager 的單例:

public static let sharedInstance: Manager = { let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders return Manager(configuration: configuration) }() 

對於其中 defaultHTTPHeaders 和 Manager 的初始化方法, 在這里就不多提了, 但是在這里有必要說明一下 SessionDelegate 這個類, 在 Manager 的初始化方法中, 調用了 SessionDelegate 的初始化方法, 返回了一個它的實例.

SessionDelegate

Responsible for handling all delegate callbacks for the underlying session.

這個類的主要作用就是處理對應 session 的所有代理回調, 它持有兩個屬性:

private var subdelegates: [Int: Request.TaskDelegate] = [:] private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT) 

subdelegates 以 task 標識符為鍵, 存儲了所有的回調. subdelegateQueue 是一個異步的隊列, 用於處理任務的回調.

Manager.sharedInstace.request

Manager 有兩個返回 Request 實例的 request 方法:

  • public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil) -> Request
  • public func request(URLRequest: URLRequestConvertible) -> 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) } 

方法中首先調用了 URLRequest 方法:

func URLRequest(method: Method, URLString: URLStringConvertible, headers: [String: String]? = nil) -> NSMutableURLRequest { let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URLString.URLString)!) mutableURLRequest.HTTPMethod = method.rawValue if let headers = headers { for (headerField, headerValue) in headers { mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField) } } return mutableURLRequest } 

首先創建一個 NSMutableURLRequest 設置它的 HTTP 請求方法和 HTTP header, 然后返回這個請求.

在請求被返回之后, 就進入了下一個環節 encode.

let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 

ParameterEncoding.encoding

ParameterEncoding 是一個用來處理一系列的參數是如何被"添加"到 URL 請求上的.

Used to specify the way in which a set of parameters are applied to a URL request.

ParameterEncoding 類型中有四種不同的編碼方法:

  • URL
  • JSON
  • PropertyList
  • Custom

其中 encode 方法就根據 ParameterEncoding 類型的不同返回不同的 NSMutableURLRequest

如果 PatameterEncoding 的類型為 URL, 那么就會把這次請求的參數以下面這種形式添加到請求的 URL 上

foo[]=1&foo[]=2  

在完成對參數的編碼之后, 就會調用另一個同名的 request 方法

request(encodedURLRequest) 

Manager.sharedInstace.request(URLRequestConvertible)

request 方法根據指定的 URL 請求返回一個 Request

Creates a request for the specified URL request.

它使用 dispatch_sync 把一個 NSURLRequest 請求同步加到一個串行隊列中, 返回一個 NSURLSessionDataTask. 並通過 session 和 dataTask 生成一個 Request 的實例.

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 } 

這段代碼還是很直觀的, 它的主要作用就是創建 Request 實例, 並發送請求.

Request.init

Request 這個類的 init 方法根據傳入的 task 類型的不同, 生成了不用類型的 TaskDelegate, 可以說是 Swift 中對於反射的運用:

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) } } 

在 UploadTaskDelegate DataTaskDelegate DownloadTaskDelegate 和 TaskDelegate幾個類的作用是處理對應任務的回調, 在 Request 實例初始化之后, 會把對應的 delegate 添加到 manager 持有的 delegate 數組中, 方便之后在對應的時間節點通知代理事件的發生.

在最后返回 request, 到這里一次網絡請求就基本完成了.

ResponseSerialization

ResponseSerialization 是用來對 Reponse 返回的值進行序列化顯示的一個 extension.

它的設計非常的巧妙, 同時可以處理 Data String 和 JSON 格式的數據,

ResponseSerializer 協議

Alamofire 在這個文件的開頭定義了一個所有 responseSerializer 都必須遵循的 protocol, 這個 protocol 的內容十分簡單, 其中最重要的就是:

var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result<SerializedObject> { get } 

所有的 responseSerializer 都必須包含 serializeResponse 這個閉包, 它的作用就是處理 response.

GenericResponseSerializer

為了同時處理不同類型的數據, Alamofire 使用泛型創建了 GenericResponseSerializer<T>, 這個結構體為處理 JSON XML 和 NSData 等數據的 responseSerializer 提供了一個骨架.

它在結構體中遵循了 ResponseSerializer 協議, 然后提供了一個 init 方法

public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result<SerializedObject>) { self.serializeResponse = serializeResponse } 

response 方法

在 Alamofire 中, 如果我們調用了 reponse 方法, 就會在 request 結束時, 添加一個處理器來處理服務器的 reponse.

這個方法有兩個版本, 第一個版本是不對返回的數據進行處理:

public func response( queue queue: dispatch_queue_t? = nil, completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Void) -> Self { delegate.queue.addOperationWithBlock { dispatch_async(queue ?? dispatch_get_main_queue()) { completionHandler(self.request, self.response, self.delegate.data, self.delegate.error) } } return self } 

該方法的實現將一個 block 追加到 request 所在的隊列中, 其它的部分過於簡單, 在這里就不多說了.

另一個版本的 response 的作用就是處理多種類型的數據.

public func response<T: ResponseSerializer, V where T.SerializedObject == V>( queue queue: dispatch_queue_t? = nil, responseSerializer: T, completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result<V>) -> Void) -> Self { delegate.queue.addOperationWithBlock { var result = responseSerializer.serializeResponse(self.request, self.response, self.delegate.data) if let error = self.delegate.error { result = .Failure(self.delegate.data, error) } dispatch_async(queue ?? dispatch_get_main_queue()) { completionHandler(self.request, self.response, result) } } return self } 

它會直接調用參數中 responseSerializer 所持有的閉包 serializeResponse, 然后返回對應的數據.

多種類型的 response 數據

有了高級的抽象方法 response, 我們現在就可以直接向這個方法中傳入不同的 responseSerializer 來產生不同數據類型的 handler

比如說 NSData

public static func dataResponseSerializer() -> GenericResponseSerializer<NSData> { return GenericResponseSerializer { _, _, data in guard let validData = data else { let failureReason = "Data could not be serialized. Input data was nil." let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason) return .Failure(data, error) } return .Success(validData) } } public func responseData(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result<NSData>) -> Void) -> Self { return response(responseSerializer: Request.dataResponseSerializer(), completionHandler: completionHandler) } 

在 ResponseSerialization.swift 這個文件中, 你還可以看到其中對於 String JSON propertyList 數據處理的 responseSerializer.

URLStringConvertible

在 ALamofire 的實現中還有一些我們可以學習的地方. 因為 Alamofire 是一個 Swift 的框架, 而且 Swift 是靜態語言, 所以有一些坑是必須要解決的, 比如說 NSURL 和 String 之間的相互轉換. 在 Alamofire 中用了一種非常優雅的解決方案, 我相信能夠給很多人帶來一定的啟發.

首先我們先定義了一個 protocol URLStringConvertible (注釋部分已經省略) :

public protocol URLStringConvertible { var URLString: String { get } } 

這個 protocol 只定義了一個 var, 遵循這個協議的類必須實現 URLString 返回 String 的這個功能.

接下來讓所有可以轉化為 String 的類全部遵循這個協議, 這個方法雖然我以前知道, 不過我還是第一次見到在實際中的使用, 真的非常的優雅:

extension String: URLStringConvertible { public var URLString: String { return self } } extension NSURL: URLStringConvertible { public var URLString: String { return absoluteString! } } extension NSURLComponents: URLStringConvertible { public var URLString: String { return URL!.URLString } } extension NSURLRequest: URLStringConvertible { public var URLString: String { return URL!.URLString } } 

這樣 String NSURL NSURLComponents 和 NSURLRequest 都可以調用 URLString 方法了. 我們也就可以直接在方法的簽名中使用 URLStringConvertible 類型.

End

到目前為止關於 Alamofire 這個框架就大致介紹完了, 框架的實現還是非常簡潔和優雅的。

 

 

轉載自:http://draveness.me/ios-yuan-dai-ma-fen-xi-alamofire/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM