Alamofire(3)— Request

😊😊😊Alamofire专题目录,欢迎及时反馈交流 😊😊😊前端


Alamofire 目录直通车 --- 和谐学习,不急不躁!swift


上一篇 Alamofire-后台下载 其中就介绍了关于 SesssionManagerSessionDelegate的分层!下面我在总结一下,而后开始一个 Alamofire 很是重要的模块 Request!数组

1、SessionManager的总结

  • SesssionManager 就是对外提供的管理者,这个管理者具有整个 Alamofire 的全部功能。安全

    • request 请求、download、upload、stream
    • 请求头信息设置以及多表单头信息设置
    • session 直接对外提供,方便自由处理
    • 初始化暴露,方便自由工厂构造,好比 URLSessionConfiguration 的配置模式
    • 重试以及适配请求
    • 闭包对外提供:backgroundCompletionHandler 后台下载回来监听闭包
    • 其中一个很是重要的点就是 SesssionManagerSession 的代理移交给了一个专门的类 : SessionDelegate
  • SessionDelegate实现了 URLSessionDelegateURLSessionTaskDelegateURLSessionDataDelegateURLSessionDownloadDelegateURLSessionStreamDelegate 等代理bash

  • 更多有意思的是:SessionDelegate还提供了对外的闭包,意味着全部的内部实现的代理状况,再外界均可以进行监听。固然这个全部的对外闭包分为两种状况:markdown

    • 在原来代理回调的内部添加闭包执行。

  • 另外一种是二选一,若是代理回来就不执行下层代理下发,执行对外闭包回调

总结:SesssionManager负责建立和管理 Request 对象及其底层NSURLSession网络

首先给你们贴出一张很是熟悉的图,看懂了这张图对下面理解 Request 的帮助大大的session

  • 从上面这张图能够看出,咱们的对外模块是SesssionManager,他给外界的用户提供了不少的功能
  • 可是这些工做的真正实现者是由iOS、Android、前端、后台、测试实现的!
  • 其中单拿 iOS 模块的任务来讲,有 首页、发现、个人、SDK、视频....模块要实现,可是咱们的项目经理有可能都不知道这些究竟是什么,怎么实现!全部来讲若是所有交给SesssionManager来实现,显然耦合性过强,还有任务乱七八糟,没有体现一个牛逼项目分层架构的效果。因此在 iOS 任务细化和SesssionManager 之间就缺了一个小管理者,对下:他知道具体事务和调度。对上:他能和SesssionManager协调配合。那就是 Request

2、Request

1. Request参数编码

  • 首先说明一下 Alamofire 支持编码格式,具体格式差别根据名字很容易理解,实在不能理解的能够自行百度查漏补缺闭包

    • URLEncoding
    • JSONEncoding
    • PropertyListEncoding
  • let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters) 经过这句代码还编码请求架构

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
}
复制代码
  • 代码自行查阅,这里总结一下

  • 首先取出请求方法,根据不一样的请求方法,参数编码是不一样的

    • .get, .head, .delete 这三个方法是把参数直接拼接到 URL 后面
    • 其余经过请求体 (httpBody) 的形式编码
  • 由于咱们的请求是经过 ASCII编码 的,因此要进行百分号编码,第一步就是对当前请求的全部路由百分号编码

  • 参数便利编码, 拼接拿出

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 有小到大进行排序
  • queryComponents 这个方法代码过分省略。
    • 里面进行递归参数,
    • key和value 取出,而后进行了百分号编码。
    • 放进元组保存,造成参数对
  • 外面讲元组加入数组中
  • map 映射 ($0)=\($1)
  • 元素之间键入一个分隔符号 &

普通方法就直接拼接到URL的后面,例如POST方法就是把这些编码好的参数对放入请求体中。其中还要加入Content-Type的请求头

2. Request内部关系梳理

探索完 request 繁杂事务之一的参数编码以后,开始分析内部:url -> request -> task 的过程。这个过程当中还创建 task以及request 之间的绑定关系

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)
    }
}
复制代码
  • 内部建立 Requestable 来帮助下层的 DataRequest 建立 Task,也是常规说法,任务分层,架构思路更清晰
  • 绑定 task 和 request , 方便在 SessionDelegate 下发任务,task 直接检索,request 方便直接获取
  • 直接任务 request.resume() 启动

  • Request 初始化的时候利用了枚举的便利性,构造建立,相关信息保存

可能不少小伙伴这个时候就会有疑虑,你不就是经过SessionDelegate实现代理, 为何这里还要有一个 DataTaskDelegate

  • 首先必定要明白 SessionDelegate 是全部 Session 的代理遵循者,任何的代理都会来到这里响应。

  • DataTaskDelegate 是咱们具体繁琐任务实现者,这里面全部方法都是由 SessionDelegate 下发响应的。

  • 说白了就是总体与局部的关系

  • SessionDelegate 是事件总响应者,可是具体的事务应该交给具体的人去执行,不能由于在 SessionDelegate 可以拿到全部的响应,那么全部的代码都罗列在这里,很显然若是那样作必然会致使混乱,咱们根据不一样的需求,响应总代理而后根据需求的不一样交给专业的人去作专业的事。耦合性大大下降,架构的分层更加明显。

  • 其中还有异步很是重要的点:delegate[task] = request 给咱们的SessionDelegate 提供一个方便及时得到能力

open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
{
  // 省略属性闭包回调的状况
    if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
        delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
    }
}
复制代码
  • 这段代码典型的经过咱们前面创建的绑定关系,经过每次代理回调的 task 找到相应的 Request 而后由于属性关系,直接拿出 Request 的属性delegate 来处理相关事务
func urlSession(
    _ session: URLSession,
    downloadTask: URLSessionDownloadTask,
    didFinishDownloadingTo location: URL)
{
    temporaryURL = location

    guard
        let destination = destination,
        let response = downloadTask.response as? HTTPURLResponse
    else { return }

    let result = destination(location, response)
    let destinationURL = result.destinationURL
    let options = result.options

    self.destinationURL = destinationURL

    do {
        if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }

        if options.contains(.createIntermediateDirectories) {
            let directory = destinationURL.deletingLastPathComponent()
            try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        }
        try FileManager.default.moveItem(at: location, to: destinationURL)
    } catch {
        self.error = error
    }
}
复制代码
  • 文件下载完毕转移存储地址,文件处理相关 以及error状况
  • 上面就是最典型的繁重恶心操做,必然是不须要被SessionDelegate 所须要知道的!

咱们从 SessionManager -> Request 这一过程明面上就是直接交付,可是背地都是经过代理响应联通的:SessionDelegate -> 具体的Request的delegate。看到这里,是否是感受很是的爽!优秀的框架老是可以给你在思想方面带来很大的提高,咱们平时开发的时候也会有很恶心的耦合度,通信问题。那么想必 Alamofire 的设计思路确定可以给你带来必定的启示!

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!

相关文章
相关标签/搜索