Alamofire之Request(一)

先上代碼,再來看我們需要了解的是啥.

  • vc:
import UIKit
import Alamofire
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        request("https://www.baidu.com", method: .get, parameters: ["username":"lb", "password":"123456"]).response { (response) in
            print("response === \(response)")
        }
        
    }

}
  • 這里直接寫 request 還是寫 SessionManager.default.request 或是 Alamofire.request 都是一樣的, 都來到 SessionManager.defaultrequest 方法里.
  • 該方法除了 url 是必傳,其他都有默認值,可以不傳.
@discardableResult
open func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    var originalRequest: URLRequest?

    do {
        originalRequest = try URLRequest(url: url, method: method, headers: headers)
        let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
        return request(encodedURLRequest)
    } catch {
        return request(originalRequest, failedWith: error)
    }
}

該方法我們一步一步來分析.

一 : 參數(shù)encode準備工作

: 先創(chuàng)建了一個原生URLRequest. 這里沒啥好說的.
: 對參數(shù) encoding 進行 encode .

查看encode方法

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
    var urlRequest = try urlRequest.asURLRequest()
    guard let parameters = parameters else { return urlRequest }

    if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
        guard let url = urlRequest.url else {
            throw AFError.parameterEncodingFailed(reason: .missingURL)
        }

        if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
            let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
            urlComponents.percentEncodedQuery = percentEncodedQuery
            urlRequest.url = urlComponents.url
        }
    } else {
        if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
        }

        urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
    }
    return urlRequest
}
  • 先判斷 method 是不是 .get, .head, .delete (具體實現(xiàn)可以點擊 encodesParametersInURL 進去查看)
  • .get, .head, .delete 則把參數(shù)直接拼接到 URL 后面.
  • 其他方式則把參數(shù)放到 httpBody 中編碼.

具體的參數(shù)拼接方式不具體講述了, 總結(jié)一下:
1 : 由于請求是通過 ASCII編碼的,所以要進行百分號編碼植旧,第一步就是對當前請求的所有路由百分號編碼.
2 : query 方法

private func query(_ parameters: [String: Any]) -> String {
    var components: [(String, String)] = []

    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        components += queryComponents(fromKey: key, value: value)
    }
    return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
  • 通過 ASCII 有小到大進行排序.
  • queryComponentskeyvalue 取出 , 進行參數(shù)遞歸,直到基礎(chǔ)數(shù)據(jù)類型,如 bool, string 热康,然后進行了百分號編碼, 之后添加到元祖返回胁后。
  • 拿到queryComponents返回的元祖數(shù)據(jù),添加到數(shù)組中.
  • 將保存了元祖的數(shù)組映射 ,元祖中數(shù)據(jù)變成 $1 = $2 形式 , 然后新生成的數(shù)組按 & 拼接成字符串.

二 : 開始請求 request(encodedURLRequest)

先點進去查看該方法:

open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
    var originalRequest: URLRequest?

    do {
        originalRequest = try urlRequest.asURLRequest()
        let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)

        let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
        let request = DataRequest(session: session, requestTask: .data(originalTask, task))

        delegate[task] = request

        if startRequestsImmediately { request.resume() }

        return request
    } catch {
        return request(originalRequest, failedWith: error)
    }
}

1. 首先 DataRequest.Requestable 方法

DataRequest.Requestable(urlRequest: originalRequest!)

Requestable 點進去

struct Requestable: TaskConvertible {
    let urlRequest: URLRequest

    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
        do {
            let urlRequest = try self.urlRequest.adapt(using: adapter)
            return queue.sync { session.dataTask(with: urlRequest) }
        } catch {
            throw AdaptError(error: error)
        }
    }
}
  • 創(chuàng)建了一個結(jié)構(gòu)體.并且初始化時就持有了 urlRequest

2. originalTask.task
調(diào)用該結(jié)構(gòu)體的 task 方法,同步創(chuàng)建了一個 URLSessionTask. ( 源碼在上面結(jié)構(gòu)體源碼中.)

3. DataRequest(session: , requestTask: )
創(chuàng)建一個 DataRequest , 自己沒有 init 方法.查找其父類 Request

init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    self.session = session

    switch requestTask {
    case .data(let originalTask, let task):
        taskDelegate = DataTaskDelegate(task: task)
        self.originalTask = originalTask
    case .download(let originalTask, let task):
        taskDelegate = DownloadTaskDelegate(task: task)
        self.originalTask = originalTask
    case .upload(let originalTask, let task):
        taskDelegate = UploadTaskDelegate(task: task)
        self.originalTask = originalTask
    case .stream(let originalTask, let task):
        taskDelegate = TaskDelegate(task: task)
        self.originalTask = originalTask
    }

    delegate.error = error
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}

這里根據(jù)不同的 requestTask 生成了不同的子類 TaskDelegate.
DataTaskDelegate / DownloadTaskDelegate 等等. 其實就是實際去干活的人. 它們與 SessionDelegate 也就是整體與局部的關(guān)系.

4. delegate[task] = request

這個 delegate 是我們的 SessionDelegate .
綁定 taskrequest , 方便在 SessionDelegate 下發(fā)任務(wù)挎袜,task 直接檢索骑素, 方便直接獲取 request.
實現(xiàn)方式點進原碼找到如下:

var requests: [Int: Request] = [:]
private let lock = NSLock()

/// Access the task delegate for the specified task in a thread-safe manner.
open subscript(task: URLSessionTask) -> Request? {
    get {
        lock.lock() ; defer { lock.unlock() }
        return requests[task.taskIdentifier]
    }
    set {
        lock.lock() ; defer { lock.unlock() }
        requests[task.taskIdentifier] = newValue
    }
}

通過一個 requests: [Int: Request] 屬性和開放出去的 subscript 下標查找方法以此實現(xiàn) taskrequest 的綁定 以及 delegate[task]setget 的實現(xiàn).

5. request.resume()
直接 request.resume() 啟動任務(wù).

下篇博客會繼續(xù)探討 request 層級結(jié)構(gòu)和具體流程.之后會統(tǒng)一進行總結(jié).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市孙技,隨后出現(xiàn)的幾起案子踱承,更是在濱河造成了極大的恐慌,老刑警劉巖攒菠,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迫皱,死亡現(xiàn)場離奇詭異,居然都是意外死亡辖众,警方通過查閱死者的電腦和手機卓起,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赵辕,“玉大人既绩,你說我怎么就攤上這事』够荩” “怎么了饲握?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我救欧,道長衰粹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任笆怠,我火速辦了婚禮铝耻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹬刷。我一直安慰自己瓢捉,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布办成。 她就那樣靜靜地躺著泡态,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迂卢。 梳的紋絲不亂的頭發(fā)上某弦,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音而克,去河邊找鬼靶壮。 笑死,一個胖子當著我的面吹牛员萍,可吹牛的內(nèi)容都是我干的腾降。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼充活,長吁一口氣:“原來是場噩夢啊……” “哼蜂莉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起混卵,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窖张,沒想到半個月后幕随,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡宿接,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年赘淮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睦霎。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡梢卸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出副女,到底是詐尸還是另有隱情蛤高,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站戴陡,受9級特大地震影響塞绿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恤批,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一异吻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喜庞,春花似錦诀浪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窄潭,卻和暖如春春宣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嫉你。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工月帝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幽污。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓嚷辅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親距误。 傳聞我的和親對象是個殘疾皇子簸搞,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359