你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS 源代碼分析——Alamofire

iOS 源代碼分析——Alamofire

編輯:IOS開發基礎

alamofire.jpg

關注倉庫,及時獲得更新:iOS-Source-Code-Analyze

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

Elegant HTTP Networking in Swift

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

  • AFNetworking 作為一個有著很多年的歷史的框架, 它雖然有著強大的社區, 不過因為時間太久了, 可能有一些歷史上的包袱。而 Alamofire 是在 Swift 誕生之後才開始出現的, 到現在為止也並沒有多長時間, 它的源代碼都是新鮮的。

  • 由於最近在寫 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 { get }

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

GenericResponseSerializer

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

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

public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result) {
    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(
    queue queue: dispatch_queue_t? = nil,
    responseSerializer: T,
    completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result) -> 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 {
    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) -> 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 這個框架就大致介紹完了, 框架的實現還是非常簡潔和優雅的, 這篇 post 從開始寫到現在也過去了好久, 寫的也不是十分的詳細具體. 如果你對這個框架的實現有興趣, 那麼看一看這個框架的源代碼也未嘗不可。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved