Alamofire中SessionManager之DataRequest详解

你好,我是Emma,今天研究的课题是Alamofire中SessionManager和DataRequest以及task之间的关系。本篇文档以SessionManager.default.request(urlString)
.response { (response) in debugPrint(response) }为切入点。

1. SessionManager类

open class SessionManager {
    public static let `default`: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()
}

其中的SessionManager.defaultHTTPHeaders指的是什么?

public static let defaultHTTPHeaders: HTTPHeaders = {
        let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
        
        let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
            let quality = 1.0 - (Double(index) * 0.1)
            return "\(languageCode);q=\(quality)"
        }.joined(separator: ", ")

        let userAgent: String = {...}()
        return [
            "Accept-Encoding": acceptEncoding,
            "Accept-Language": acceptLanguage,
            "User-Agent": userAgent
        ]
    }()

初始化方法:

open class SessionManager {
    public init(
        configuration: URLSessionConfiguration = URLSessionConfiguration.default,
        //原生和Alomofire之间的代理移交
        delegate: SessionDelegate = SessionDelegate(),
        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
        //设置代理这个类的delegate 就是 SessionDelegate = SessionDelegate(),做的是移交工作。
        //这里是如何实现将UIApplicationDelegate后台的方法桥接到URLSessionDelegate中的初始化方法。这点体现了SessionDelegate这个类的强大之处。
        self.delegate = delegate
        //初始化这个类的URLSession
        self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
        //提供给SessionDelegate的一些变量初始化
        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
}

commonInit方法的具体实现:

//好吧,被SessionDelegate 的set方法附身之感
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
    session.serverTrustPolicyManager = serverTrustPolicyManager
//初始化中有self.delegate = delegate,这里有delegate.sessionManager持有self不会造成循环引用吗?不会。原因 `weak var sessionManager: SessionManager?`
    delegate.sessionManager = self

//这个主要是给这个类提供backgroundCompletionHandler?()属性的作用。
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
        guard let strongSelf = self else { return }
        DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
    }
}

SessionManager与SessionDelegate的关联小节:

open class SessionManager {
    public let delegate: SessionDelegate 
    public init?(delegate: SessionDelegate)
    {
        self.delegate = delegate
        commonInit()
    }
    private func commonInit() {
        delegate.sessionManager = self
    }
}
open class SessionDelegate: NSObject {
    weak var sessionManager: SessionManager?
}
//这样双方互相持有但是是打破循环链的持有。可以在SessionDelegate中做一些关于SessionManager的容错处理。

2.Request之DataRequest

下面进入今天的正题Request之DataRequest,首先看request方法:

@discardableResult
open func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,//指定是请求的方法类型
    parameters: Parameters? = nil,//参数
    encoding: ParameterEncoding = URLEncoding.default,//编码,此处默认的是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)
    }
}

方法的百分号编码和区分方法的具体实现:

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
    var urlRequest = try urlRequest.asURLRequest()
//判断是否有参数
    guard let parameters = parameters else { return urlRequest }
//根据HTTPMethod这个枚举进行派分
    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
}

private func query(_ parameters: [String: Any]) -> String {
    //创建元祖
    var components: [(String, String)] = []
    //遍历
    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        //进行递归操作去除key,value
        components += queryComponents(fromKey: key, value: value)
    }
    //joined添加&分隔符,实例:A&B&C
    return components.map { "\($0)=\($1)" }.joined(separator: "&")
    
}

从request方法闭包中的return request(encodedURLRequest)方法可以看出走的是下面这步

@discardableResult
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)
        //重点点击进入下面代码,RequestTask是枚举,区分了.data,.download,.stream,.upload
        let request = DataRequest(session: session, requestTask: .data(originalTask, task))
        //task和Request是意义对应的关联关系。
        delegate[task] = request
        if startRequestsImmediately { request.resume() }
        return request
    } catch {
        return request(originalRequest, failedWith: error)
    }
}
//保存了DataRequest的task适配器请求序列
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)
            //重点是通过请求找到dataTask
            return queue.sync { session.dataTask(with: urlRequest) }
        } catch {
            throw AdaptError(error: error)
        }
    }
}

这个Request的初始化主要是一些与task的代理之间的持有关系和保存一些值。

open class Request{
    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    //session这是是用来进行保存session的。
        self.session = session
    //  requestTask是一个枚举,进行任务和代理的分发
        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
        //此处这个线程添加了addOperation ,其初始化见下面代码。
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }
}
open class TaskDelegate: NSObject {
    init(task: URLSessionTask?) {
    //关于task代理中线程的初始化
        self.queue = {
            let operationQueue = OperationQueue() 
            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility
            return operationQueue
        }()
    }
}

总结:

1)delegate[task] = request这样设计的原因是什么?

原因是实现SessionManager和Request之间的解耦。

2)那么SessionManager,Request,Task之间各自负责什么职能又是如何联系的?

SessionManager通过SessionDelegate来做请求的主体序列,通过这个来塞入一些头信息。请求体就有DataRequest等Request来负责,他负责的是一些URL,params,Method,Ecode,header等的打包,请求得开线程啊,线程这部分就分离出来由task负责,并且是Request和task是一对一的对练关系。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容