美文网首页
Alamofire(五)-- Task代理

Alamofire(五)-- Task代理

作者: Daniel_Harvey | 来源:发表于2019-08-22 13:42 被阅读0次

    前言

    这篇文章,咱们来说说Task代理,通过之前的文章,我们可以知道一个普通的网络请求过程是:

    • 根据一个URL和若干的参数来生成Request
    • 根据Request生成一个会话Session
    • 再根据这个Session生成Task
    • 我们开启Task就完成了这个请求。

    在这个请求过程中,还有重定向、数据的上传、证书的验证、配置等信息。

    其实,我们做iOS开发的,对代理这个问题,不管是在网络请求中,还是用于代理回调等地方,我觉得代理就好比是一个拥有较高权限的管理员。这种方式在我们做业务开发中,是很好的处理方式。

    URLSessionTask

    Task分类

    在苹果原生网络框架中,URLSessionTask是最基础的task任务封装,主要有以下几种task

    • URLSessionDataTask
    • URLSessionUploadTask
    • URLSessionDownloadTask
    • URLSessionStreamTask
      注意: URLSessionStreamTask这个我们先暂时不介绍,在后面的文章中单独介绍说明。

    继承关系

    我们先把URLSessionTask的相关继承关系图给到大家看一下,具体代理方法我们后面慢慢讲。

    URLSessionTask

    URLSessionTask子类继承关系:

    URLSessionTask子类.png

    URLSessionTaskDelegate

    URLSessionTaskDelegate继承自URLSessionDelegateURLSessionTaskDelegate的主要协议方法有:

    optional func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void)
    
    optional func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    
    optional func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
    
    optional func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
    
    optional func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
    // 上传比较特殊一点,只有这一个跟上传相关的代理方法
    

    URLSessionDataDelegate

    URLSessionDataDelegate则是继承自URLSessionTaskDelegate协议,它的主要协议方法有:

    optional func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
    
    optional func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask)
    
    optional func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
    
    optional func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void)
    

    URLSessionDownloadDelegate

    URLSessionDownloadDelegate同样是继承自URLSessionTaskDelegate协议,主要方法有:

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
    
    optional func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
    
    optional func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64)
    

    TaskDelegate

    Alamofire中,咱们可以看到,TaskDelegate类继承自NSObject,处于继承链的最底层,它为我们提供了一些基础的属性,而且,这些属性是和其他的Delegate共享使用:

    // MARK: Properties
    
        /// The serial operation queue used to execute all operations after the task completes.
        public let queue: OperationQueue
    
        /// The data returned by the server.
        public var data: Data? { return nil }
    
        /// The error generated throughout the lifecyle of the task.
        public var error: Error?
    
        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?
        var metrics: AnyObject? // URLSessionTaskMetrics
    
        private var _task: URLSessionTask? {
            didSet { reset() }
        }
    
        private let taskLock = NSLock()
    

    我们接下来分析一下这些属性。

    属性简介

    queue

    queue: OperationQueue,显而易见,这就是一个队列,在队列中,我们可以添加很多的operation,而且,当我们把isSuspended的值设置为true,就可以让队列中的所有operation暂停,如果想要继续执行operation,我们需要把isSuspended的值设置为false
    Alamofire框架中,有以下几种情况,会加入该队列的operation

    • 队列在任务完成后,把isSuspended的值设置为false
    • 在任务完成后调用RequestendTime,就可以为Request设置请求结束时间。
    • 在处理response中,Alamofire中的响应回调是链式的,原理就是把这些回调函数通过operation添加到队列中,因此也保证了回调函数的访问顺序是正确的。
    • 还有上传数据成功后,删除一些临时文件等操作。

    data

    data: Data?,服务器返回的Data,?表示这个可能为空。

    error

    error: Error?,这个应该很好理解了,在网络请求过程中,很有可能出现错误,那么我们添加这个属性,就是为了抓取过程中出现的错误。

    task

    task: URLSessionTask?,就是表示一个task,很重要的属性。

    initialResponseTime

    initialResponseTime: CFAbsoluteTime?,这是一个task的响应时间,如果是URLSessionDataTask,则表示接收到数据的时间;如果是URLSessionDownloadTask,则表示开始写数据的时间;如果是URLSessionUploadTask,则表示上传数据的时间。

    credential

    credential: URLCredential?,这个表示证书,在做证书验证的时候用到。

    metrics

    metrics: AnyObject?,这是苹果提供的一个统计task信息的类URLSessionTaskMetrics,它可以统计相关任务的事务,任务开始时间、结束时间,以及重定向的次数等信息。

    生命周期Lifecycle

    首先,还是来看一看代码:

    // MARK: Lifecycle
    
        init(task: URLSessionTask?) {
            _task = task
    
            self.queue = {
                let operationQueue = OperationQueue()
    
                operationQueue.maxConcurrentOperationCount = 1
                operationQueue.isSuspended = true
                operationQueue.qualityOfService = .utility
    
                return operationQueue
            }()
        }
    
        func reset() {
            error = nil
            initialResponseTime = nil
        }
    

    init函数

    在初始化函数中,主要就是设置operationQueueoperationQueue.isSuspended = true,就可以保证队列中的operation都是暂停的,通常情况下,operation在被加入到队列中后,会立即执行。

    reset函数

    reset函数把errorinitialResponseTime都置为nil,这个就很简单了,不说了。

    URLSessionTaskDelegate

    接下来,我们看看URLSessionTaskDelegate的相关函数:

    // MARK: URLSessionTaskDelegate
    
        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)?
    

    taskWillPerformHTTPRedirection

    首先来看一下第一个代理方法:

    @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
    
            if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
                redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
            }
    
            completionHandler(redirectRequest)
        }
    

    根据函数名的意思,我们应该知道,这个函数就是用来处理重定向问题的,这个函数要求返回一个redirectRequest,顾名思义,就是重定向Request的,处理方式就是:如果给代理的重定向函数赋值了,就会返回代理函数的返回值,否则返回服务器的Request

    taskDidReceiveChallenge

    这个方法就是用来处理请求验证相关的,看一下:

    @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 {
                (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
            } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                let host = challenge.protectionSpace.host
    
                if
                    let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
                    let serverTrust = challenge.protectionSpace.serverTrust
                {
                    if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                        disposition = .useCredential
                        credential = URLCredential(trust: serverTrust)
                    } else {
                        disposition = .cancelAuthenticationChallenge
                    }
                }
            } else {
                if challenge.previousFailureCount > 0 {
                    disposition = .rejectProtectionSpace
                } else {
                    credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
    
                    if credential != nil {
                        disposition = .useCredential
                    }
                }
            }
    
            completionHandler(disposition, credential)
        }
    

    首先,我们来看一下disposition,它的类型是URLSession.AuthChallengeDisposition,这个类型就是一个枚举类型:

    @available(iOS 7.0, *)
        public enum AuthChallengeDisposition : Int {
    
            
            case useCredential /* Use the specified credential, which may be nil */
    
            case performDefaultHandling /* Default handling for the challenge - as if this delegate were not implemented; the credential parameter is ignored. */
    
            case cancelAuthenticationChallenge /* The entire request will be canceled; the credential parameter is ignored. */
    
            case rejectProtectionSpace /* This challenge is rejected and the next authentication protection space should be tried; the credential parameter is ignored. */
        }
    
    • useCredential:使用的证书
    • performDefaultHandling:采用默认的方式,和服务器返回的authenticationMethod有很大关系
    • cancelAuthenticationChallenge:取消认证
    • rejectProtectionSpace:拒绝认证

    我们在进行验证的时候,有三种验证方式:

    • 客户端验证
    • 服务器验证
    • 双向验证

    从上面的函数方法,可以知道,如果服务器需要验证客户端,只需要给TaskDelegatetaskDidReceiveChallenge赋值就可以了。

    Alamofire的双向验证中,客户端和服务端如果需要建立SSL,只需要2步可以完成:

    • 服务端返回WWW-Authenticate响应头,并返回自己信任证书
    • 客户端验证证书,然后用证书中的公钥把数据加密后发送给服务端

    taskNeedNewBodyStream

    同样的,我们先来看一下代码:

    @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)
        }
    

    当给taskRequest提供一个body stream时才会调用,我们不需要关心这个方法,即使我们通过fileURL或者NSData上传数据时,该函数也不会被调用。

    taskDidCompleteWithError

    一样的,先看代码:

    @objc(URLSession:task:didCompleteWithError:)
        func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
            if let taskDidCompleteWithError = taskDidCompleteWithError {
                taskDidCompleteWithError(session, task, error)
            } else {
                if let error = error {
                    if self.error == nil { self.error = error }
    
                    if
                        let downloadDelegate = self as? DownloadTaskDelegate,
                        let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                    {
                        downloadDelegate.resumeData = resumeData
                    }
                }
    
                queue.isSuspended = false
            }
        }
    

    该函数在请求完成后被调用,值得注意的是error不为nil的情况,除了给自身的error属性赋值外,针对下载任务做了特殊处理,就是把当前已经下载的数据保存在downloadDelegate.resumeData中,有点像断点下载。

    DataTaskDelegate

    属性

    首先,还是先来看一下它的属性:

    // MARK: Properties
    
        var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
    
        override var data: Data? {
            if dataStream != nil {
                return nil
            } else {
                return mutableData
            }
        }
    
        var progress: Progress
        var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
    
        var dataStream: ((_ data: Data) -> Void)?
    
        private var totalBytesReceived: Int64 = 0
        private var mutableData: Data
    
        private var expectedContentLength: Int64?
    
    • dataTask: URLSessionDataTaskDataTaskDelegate管理URLSessionDataTask
    • data: Data?:同样是返回Data,但这里有一点不同,如果定义了dataStream方法的话,这个data返回为nil
    • progress: Progress:进度,这个就不解释了
    • progressHandler:这不是函数,是一个元组,我们等下具体说说
    • dataStream:自定义的数据处理函数
    • totalBytesReceived:已经接收的数据
    • mutableData:保存数据的容器
    • expectedContentLength:接收的数据的总大小

    生命周期

    先看代码:

    // MARK: Lifecycle
    
        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
        }
    

    这些很简单的,没有什么可说的。

    方法调用

    主要是看看它的函数方法:

    // MARK: URLSessionDataDelegate
    
        var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
        var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
        var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
        var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
    

    dataTaskDidReceiveResponse

    func urlSession(
            _ session: URLSession,
            dataTask: URLSessionDataTask,
            didReceive response: URLResponse,
            completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
        {
            var disposition: URLSession.ResponseDisposition = .allow
    
            expectedContentLength = response.expectedContentLength
    
            if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
                disposition = dataTaskDidReceiveResponse(session, dataTask, response)
            }
    
            completionHandler(disposition)
        }
    

    当收到服务端的响应后,该方法被触发。在这个函数中,我们能够获取到和数据相关的一些参数。

    dataTaskDidBecomeDownloadTask

    func urlSession(
            _ session: URLSession,
            dataTask: URLSessionDataTask,
            didBecome downloadTask: URLSessionDownloadTask)
        {
            dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
        }
    

    dataTaskDidReceiveResponse方法中,disposition的类型是URLSession.ResponseDisposition,这是一个枚举类型:

    @available(iOS 7.0, *)
        public enum ResponseDisposition : Int {
    
            
            case cancel /* Cancel the load, this is the same as -[task cancel] */
    
            case allow /* Allow the load to continue */
    
            case becomeDownload /* Turn this request into a download */
    
            @available(iOS 9.0, *)
            case becomeStream /* Turn this task into a stream task */
        }
    

    因此,当我们设置成becomeDownload时,dataTaskDidBecomeDownloadTask方法就会被调用,创建了一个新的downloadTask

    dataTaskDidReceiveData

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
            if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
    
            if let dataTaskDidReceiveData = dataTaskDidReceiveData {
                dataTaskDidReceiveData(session, dataTask, data)
            } else {
                if let dataStream = dataStream {
                    dataStream(data)
                } else {
                    mutableData.append(data)
                }
    
                let bytesReceived = Int64(data.count)
                totalBytesReceived += bytesReceived
                let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
    
                progress.totalUnitCount = totalBytesExpected
                progress.completedUnitCount = totalBytesReceived
    
                if let progressHandler = progressHandler {
                    progressHandler.queue.async { progressHandler.closure(self.progress) }
                }
            }
        }
    

    这个方法会把数据放入对象中,对自定义函数和进度信息进行处理。

    dataTaskWillCacheResponse

    func urlSession(
            _ session: URLSession,
            dataTask: URLSessionDataTask,
            willCacheResponse proposedResponse: CachedURLResponse,
            completionHandler: @escaping (CachedURLResponse?) -> Void)
        {
            var cachedResponse: CachedURLResponse? = proposedResponse
    
            if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
                cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
            }
    
            completionHandler(cachedResponse)
        }
    

    该函数用于处理是否需要缓存响应,Alamofire默认是缓存这些response的,但是每次发请求,它不会再缓存中读取。

    DownloadTaskDelegate

    属性

    // MARK: Properties
    
        var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask }
    
        var progress: Progress
        var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
    
        var resumeData: Data?
        override var data: Data? { return resumeData }
    
        var destination: DownloadRequest.DownloadFileDestination?
    
        var temporaryURL: URL?
        var destinationURL: URL?
    
        var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL }
    

    有和上面重复的属性,我就不说了,看下没有重复的部分:

    downloadTask:和URLSessionDownloadDelegate相对应的URLSessionDownloadTask

    • resumeData:在上边我们提到过,当请求完成后,如果error不为nil,如果是DownloadTaskDelegate,就会给这个属性赋值
    • data:返回resumeData
    • destination:通过这个函数可以自定义文件保存目录和保存方式,这个保存方式分两种,为URl创建文件夹,删除已经下载且存在的文件,这个会在后续的文章中提到
      temporaryURL:临时的URL
      destinationURL:数据存储URL
      fileURLfileURL返回文件的路径,如果destination不为nil,就返回destinationURL,否则返回temporaryURL

    生命周期

    // MARK: Lifecycle
    
        override init(task: URLSessionTask?) {
            progress = Progress(totalUnitCount: 0)
            super.init(task: task)
        }
    
        override func reset() {
            super.reset()
    
            progress = Progress(totalUnitCount: 0)
            resumeData = nil
        }
    

    方法调用

    代理方法有三个:

    // MARK: URLSessionDownloadDelegate
    
        var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
        var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
        var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
    

    downloadTaskDidFinishDownloadingToURL

    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
            }
        }
    

    当数据下载完成后,该函数被触发。系统会把数据下载到一个临时的locationURL的地方,我们就是通过这个URL拿到数据的。上边函数内的代码主要是把数据复制到目标路径中。

    downloadTaskDidWriteData

    func urlSession(
            _ session: URLSession,
            downloadTask: URLSessionDownloadTask,
            didWriteData bytesWritten: Int64,
            totalBytesWritten: Int64,
            totalBytesExpectedToWrite: Int64)
        {
            if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
    
            if let downloadTaskDidWriteData = downloadTaskDidWriteData {
                downloadTaskDidWriteData(
                    session,
                    downloadTask,
                    bytesWritten,
                    totalBytesWritten,
                    totalBytesExpectedToWrite
                )
            } else {
                progress.totalUnitCount = totalBytesExpectedToWrite
                progress.completedUnitCount = totalBytesWritten
    
                if let progressHandler = progressHandler {
                    progressHandler.queue.async { progressHandler.closure(self.progress) }
                }
            }
        }
    

    该代理方法在数据下载过程中被触发,主要的作用就是提供下载进度。

    downloadTaskDidResumeAtOffset

    func urlSession(
            _ session: URLSession,
            downloadTask: URLSessionDownloadTask,
            didResumeAtOffset fileOffset: Int64,
            expectedTotalBytes: Int64)
        {
            if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
                downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
            } else {
                progress.totalUnitCount = expectedTotalBytes
                progress.completedUnitCount = fileOffset
            }
        }
    

    如果一个下载的task是可以恢复的,那么当下载被取消或者失败后,系统会返回一个resumeData对象,这个对象包含了一些跟这个下载task相关的一些信息,有了它就能重新创建下载task,创建方法有两个:downloadTask(withResumeData:)downloadTask(withResumeData:completionHandler:),当task开始后,上边的代理方法就会被触发。

    UploadTaskDelegate

    属性

    // MARK: Properties
    
        var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask }
    
        var uploadProgress: Progress
        var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
    

    这些属性又得已经重复了,就不多说了。

    生命周期

    // MARK: Lifecycle
    
        override init(task: URLSessionTask?) {
            uploadProgress = Progress(totalUnitCount: 0)
            super.init(task: task)
        }
    
        override func reset() {
            super.reset()
            uploadProgress = Progress(totalUnitCount: 0)
        }
    

    这也不用多说啥,主要的还是看看方法调用。

    方法调用

    这里只有一个方法调用:

    // MARK: URLSessionTaskDelegate
    
        var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
    
        func URLSession(
            _ session: URLSession,
            task: URLSessionTask,
            didSendBodyData bytesSent: Int64,
            totalBytesSent: Int64,
            totalBytesExpectedToSend: Int64)
        {
            if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
    
            if let taskDidSendBodyData = taskDidSendBodyData {
                taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
            } else {
                uploadProgress.totalUnitCount = totalBytesExpectedToSend
                uploadProgress.completedUnitCount = totalBytesSent
    
                if let uploadProgressHandler = uploadProgressHandler {
                    uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
                }
            }
        }
    

    该函数主要目的是提供上传的进度,在Alamofire中,上传数据用的是stream,这个会在后续文章中给出详细的解释。

    总结

    学习框架的时候,好多东西当时记住了,可能一会儿就忘了,写下这篇文章的目的,就是为了加深理解印象,方便以后查阅笔记,如果文章有错误,还望指出,还得感谢一下这位朋友马在路上

    相关文章

      网友评论

          本文标题:Alamofire(五)-- Task代理

          本文链接:https://www.haomeiwen.com/subject/osjnsctx.html