【Alamofire源码解析】11 - TaskDelegate

这个文件定义了几个delegate类型。TaskDelegateDataTaskDelegateDownloadTaskDelegate的父类;其中DataTaskDelegateUploadTaskDelegate的父类。

1. TaskDelegate

SessionDelegate类似,TaskDelegate用于处理task的所有回调和执行队列的operations。

1) 属性和初始化方法

// 任务完成后,执行这个队列里的所有operations
open let queue: OperationQueue

// 服务器返回的数据,这里返回nil,然后让子类去重写
public var data: Data? { return nil }

// 整个任务过程中出现的错误
public var error: Error?

// 作者为了在get和set的时候加锁,定义了一个私有的_task
var task: URLSessionTask? {
    set {
        taskLock.lock(); defer { taskLock.unlock() }
        _task = newValue
    }
    get {
        taskLock.lock(); defer { taskLock.unlock() }
        return _task
    }
}

var initialResponseTime: CFAbsoluteTime? // 服务器最开始响应的时间
var credential: URLCredential? // URL证书
var metrics: AnyObject? // URLSessionTaskMetrics

private var _task: URLSessionTask? {
    didSet { reset() }
}

private let taskLock = NSLock()

init(task: URLSessionTask?) {
    _task = task

    // 这里利用了一个closure来初始化queue
    // 我们可以学习这种模式,可以让代码看起来更清晰
    self.queue = {
        let operationQueue = OperationQueue()

        operationQueue.maxConcurrentOperationCount = 1
        // 刚初始化,把这个属性设置为true暂时不执行队列里面的operation
        operationQueue.isSuspended = true
        operationQueue.qualityOfService = .utility

        return operationQueue
    }()
}

func reset() {
    error = nil
    initialResponseTime = nil
}

2) 实现URLSessionTaskDelegate

// 定义了与回调对应的closure
var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?

// 告诉代理服务器请求HTTP重定向
// @objc的意思是告诉在OC使用的时候如何显示这个方法
@objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
func urlSession(
    _ session: URLSession,
    task: URLSessionTask,
    willPerformHTTPRedirection response: HTTPURLResponse,
    newRequest request: URLRequest,
    completionHandler: @escaping (URLRequest?) -> Void)
{
    var redirectRequest: URLRequest? = request

    // 如果提供了taskWillPerformHTTPRedirection,则更新redirectRequest
    if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
        redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
    }

    completionHandler(redirectRequest)
}

// 服务器请求认证
@objc(URLSession:task:didReceiveChallenge:completionHandler:)
func urlSession(
    _ session: URLSession,
    task: URLSessionTask,
    didReceive challenge: URLAuthenticationChallenge,
    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
    // 初始化一个默认的配置,就像客户端没有实现这个回调
    var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
    var credential: URLCredential?

    if let taskDidReceiveChallenge = taskDidReceiveChallenge {
        // 如果taskDidReceiveChallenge,更新disposition和credential
        (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
    } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { // 认证方法是ServerTrust
        
        // 获取保护区域的主机地址
        let host = challenge.protectionSpace.host

        // 判断我们是否提供了服务器信任策略
        if let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
            let serverTrust = challenge.protectionSpace.serverTrust
        {
            // 评估serverTrust对这个主机地址是否有效
            if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                // 评估通过,把disposition更新为使用证书;并更新证书
                disposition = .useCredential
                credential = URLCredential(trust: serverTrust)
            } else {
                // 评估不通过,取消认证,整个请求也会被取消
                disposition = .cancelAuthenticationChallenge
            }
        }
    } else {
        if challenge.previousFailureCount > 0 { // 之前认证失败的次数大于0
            // 更新disposition为rejectProtectionSpace,当前这个challenge被拒绝
            disposition = .rejectProtectionSpace
        } else { // 之前认证失败的次数不大于0
            
            // 使用证书进行认证
            credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)

            if credential != nil {
                disposition = .useCredential
            }
        }
    }

    // 最后执行completionHandler
    completionHandler(disposition, credential)
}

// 服务器请求新的inputStream
@objc(URLSession:task:needNewBodyStream:)
func urlSession(
    _ session: URLSession,
    task: URLSessionTask,
    needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
    var bodyStream: InputStream?

    if let taskNeedNewBodyStream = taskNeedNewBodyStream {
        bodyStream = taskNeedNewBodyStream(session, task)
    }

    completionHandler(bodyStream)
}

// 任务完成
@objc(URLSession:task:didCompleteWithError:)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    // 如果有taskDidCompleteWithError,则执行
    if let taskDidCompleteWithError = taskDidCompleteWithError {
        taskDidCompleteWithError(session, task, error)
    } else { // 没有taskDidCompleteWithError
        if let error = error {
            
            // 设置error
            if self.error == nil { self.error = error }

            // 如果error不为空的话,userInfo里面携带有resumeData
            if let downloadDelegate = self as? DownloadTaskDelegate,
                let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
            {
                downloadDelegate.resumeData = resumeData
            }
        }
        
        // 执行队列里的所有operations
        queue.isSuspended = false
    }
}

2. DataTaskDelegate

1) 属性和初始化方法

var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }

// 重写父类的属性,服务器返回的数据
override var data: Data? {
    if dataStream != nil { // 如果有dataStream去处理data,这里的data就返回nil
        return nil
    } else {
        return mutableData // 返回从服务器接收到的数据
    }
}

var progress: Progress // 进度
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?

var dataStream: ((_ data: Data) -> Void)? // 处理数据的closure

private var totalBytesReceived: Int64 = 0
private var mutableData: Data // 用于存储从服务器接收到的数据

private var expectedContentLength: Int64? // 服务器文件的总大小

override init(task: URLSessionTask?) {
    mutableData = Data()
    progress = Progress(totalUnitCount: 0)

    super.init(task: task)
}

override func reset() {
    super.reset()

    progress = Progress(totalUnitCount: 0)
    totalBytesReceived = 0
    mutableData = Data()
    expectedContentLength = nil
}

3. DownloadTaskDelegate & UploadTaskDelegate

这两个delegate与DataTaskDelegate非常类似,大家可以自己看下,如果有不懂的,欢迎留言,我会尽力解答。

有任何问题,欢迎大家留言!

欢迎加入我管理的Swift开发群:536353151,本群只讨论Swift相关内容。

原创文章,转载请注明出处。谢谢!

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 概述 上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation,本篇主...
    egoCogito_panf阅读 1,922评论 0 13
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • 心灵的选择需要心中的一秆天平去称量,任何大的偏失和差错都会给你的心灵留下永不消褪的污渍和伤痕。
    spring小公举阅读 155评论 0 0
  • 飞机,张开翅膀的飞鸟 带走了思念和雀跃 忘记孤寂的翱翔 落地便是花开 路口,左右交叉的十字路口 红绿灯间隔的闪烁 ...
    38ffe2ae3f02阅读 367评论 8 11