Alamofire源碼學習(五): Request的四個子類

往期導航:

Alamofire源碼學習目錄合集swift

Request基類不能被直接使用, 而是使用他的四個子類:api

  1. DataRequest
    • 使用URLSessionDataTask請求數據, 數據保存在內存中, 使用Data對象儲存
  2. DataStreamRequest
    • 使用URLSessionDataTask請求數據, 使用OutputStream傳出數據
  3. DownloadRequest
    • 使用URLSessionDownloadTask下載數據, 文件下載在磁盤上
  4. UploadRequest
    • 使用URLSessionUploadTask上傳請求, 上傳內容能夠用表單, 文件, InputStream

這四個子類只是定義了一些初始化以及一些必要的方法, 在ResponseSerialization分別對他們作了擴展, 用來解析處理數據使用數組

DataRequest

DataRequest子類是最基本的一個子類, 請求到的數據都使用Data對象寫在內存裏, 適合普通請求, 小圖片請求, 小文件請求緩存

public class DataRequest: Request {
    /// 用來建立URLRequest對象的協議對象
    public let convertible: URLRequestConvertible
    /// 開放給外部的計算屬性
    public var data: Data? { mutableData }

    /// 私有的線程安全的Data對象, 保存請求的數據
    @Protected
    private var mutableData: Data? = nil

    /// 初始化方法比父類多了1個參數: 用來初始化URLRequest對象的URLRequestConvertible
    init(id: UUID = UUID(), convertible: URLRequestConvertible, underlyingQueue: DispatchQueue, serializationQueue: DispatchQueue, eventMonitor: EventMonitor?, interceptor: RequestInterceptor?, delegate: RequestDelegate) {
        self.convertible = convertible

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    /// 重置請求時要把已下載的data清空
    override func reset() {
        super.reset()

        mutableData = nil
    }

    /// Called when `Data` is received by this instance.
    ///
    /// - Note: Also calls `updateDownloadProgress`.
    ///
    /// - Parameter data: The `Data` received.
    /// 當task收到data時, 由SessionDelegate調用, 保存數據
    func didReceive(data: Data) {
        if self.data == nil {
            //初始
            mutableData = data
        } else {
            //追加
            $mutableData.write { $0?.append(data) }
        }
        //更新下載進度
        updateDownloadProgress()
    }
    /// 必須實現的父類方法, 返回DataRequest對應的Task
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        // 由於URLRequest在swift裏是struct, 因此先用賦值來複制一下
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }

    /// 更新下載進度(已下載字節/待下載字節)
    func updateDownloadProgress() {
        let totalBytesReceived = Int64(data?.count ?? 0)
        let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown

        downloadProgress.totalUnitCount = totalBytesExpected
        downloadProgress.completedUnitCount = totalBytesReceived
        // 在進度回調隊列調用進度回調
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }

    /// Validates the request, using the specified closure.
    ///
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter validation: `Validation` closure used to validate the response.
    ///
    /// - Returns: The instance.
    /// 添加有效性判斷回調, 用來判斷當前URLRequest,URLSessionTask,下載的Data是否有效
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        //建立無參處理閉包
        let validator: () -> Void = { [unowned self] in
            //不能有錯誤, 必須有響應
            guard self.error == nil, let response = self.response else { return }
            //調用判斷回調
            let result = validation(self.request, response, self.data)
            //有錯誤就設置給error
            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }
            //通知監聽器判斷有效性完成
            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       data: self.data,
                                       withResult: result)
        }
        //把閉包加入到數組
        $validators.write { $0.append(validator) }

        return self
    }
}
複製代碼

DataStreamRequest

DataStreamRequest相似於DataRequest, 不過沒有使用一個Data保存所有數據, 而是將受到的數據封裝成DataStreamRequest.Stream類型(包括Data,完成,錯誤),並持有N個用來處理數據流的閉包,在受到請求數據時,對data封裝而後使用閉包交給調用方處理。安全

  • 在ResponseSerialization中對DataStreamRequest調用解析方法時,會把建立封裝Data成DataStreamRequest.Stream類型的回調並保存,當收到數據時,對所有的回調逐個調用,每處理完一個Stream就刪除一個回調,所有處理完成後,調用完成回調。
  • 支持建立一對IOStream,若是選擇建立IOStream,請求會當即發送,調用方會得到InputStream,DataStreamRequest使用對應的OutputStream往外寫數據,上層使用InputStream接收
  • 由於使用Stream處理,所以沒有把Data所有寫在內存裏,上層能夠根據須要,轉換爲Data保存在內存裏,也能夠直接寫入到磁盤中保存。
/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure.
public final class DataStreamRequest: Request {
    /// 定義調用方用來處理Stream的回調, 該回調能夠拋出錯誤, 能夠控制拋出錯誤時是否取消請求
    public typealias Handler<Success, Failure: Error> = (Stream<Success, Failure>) throws -> Void

    /// 封裝了數據流對象+取消token,用來給上層處理數據使用
    public struct Stream<Success, Failure: Error> {
        /// 最新的流事件(數據或者錯誤)
        public let event: Event<Success, Failure>
        /// 用來取消數據流的token, 其實只是封裝了DataStreamRequest對象, 有一個cancel方法
        public let token: CancellationToken

        /// 取消數據流(取消請求)
        public func cancel() {
            token.cancel()
        }
    }

    /// 封裝了數據流, 包括: 數據, 錯誤, 完成三種
    public enum Event<Success, Failure: Error> {
        /// 數據或者錯誤
        case stream(Result<Success, Failure>)
        /// 完成信息(完成狀態)(多是請求完成, 請求取消, 請求出錯)
        case complete(Completion)
    }

    /// 當數據流完成時, 攜帶在Event裏的數據
    public struct Completion {
        /// 最後發出的請求
        public let request: URLRequest?
        /// 最後收到的響應
        public let response: HTTPURLResponse?
        /// 最後收到的請求指標
        public let metrics: URLSessionTaskMetrics?
        /// 數據流出錯的錯誤
        public let error: AFError?
    }

    /// 用來取消數據流時, 取消請求的token
    public struct CancellationToken {
        weak var request: DataStreamRequest?

        init(_ request: DataStreamRequest) {
            self.request = request
        }

        /// 取消數據流時直接取消請求
        public func cancel() {
            request?.cancel()
        }
    }

    /// 用來建立
    public let convertible: URLRequestConvertible
    /// 若是數據流解析出錯是否直接取消請求, 默認爲false
    public let automaticallyCancelOnStreamError: Bool

    /// 包裝須要線程安全操做的幾個數據
    struct StreamMutableState {
        /// 當把DataStreamRequest做爲InputStream時, 會建立該對象並把輸出流轉綁定給InputStream
        var outputStream: OutputStream?
        /// 內部用來處理Data的數據流回調數組, 主要工做爲: 封裝Stream對象交給上層回調處理, 而後記錄正在處理的數據流個數, 所有處理完以後須要執行完成回調
        var streams: [(_ data: Data) -> Void] = []
        /// 當前正在執行的流的個數
        var numberOfExecutingStreams = 0
        /// 當數據流還沒完成時暫存完成回調的數組
        var enqueuedCompletionEvents: [() -> Void] = []
    }

    @Protected
    var streamMutableState = StreamMutableState()

    /// 比父類多了兩個參數: 1.URLRequestConvertible, 2. 是否在Stream處理失敗時取消請求
    init(id: UUID = UUID(), convertible: URLRequestConvertible, automaticallyCancelOnStreamError: Bool, underlyingQueue: DispatchQueue, serializationQueue: DispatchQueue, eventMonitor: EventMonitor?, interceptor: RequestInterceptor?, delegate: RequestDelegate) {
        self.convertible = convertible
        self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    /// task也是URLSessionDataTask
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }

    /// 完成時要把OutputStream關閉
    override func finish(error: AFError? = nil) {
        $streamMutableState.write { state in
            state.outputStream?.close()
        }

        super.finish(error: error)
    }
    /// 收到Data的處理
    func didReceive(data: Data) {
        $streamMutableState.write { state in
            if let stream = state.outputStream {
                // 若是有做爲InputStream, 就會建立對應的OutputStream, 輸出數據
                underlyingQueue.async {
                    var bytes = Array(data)
                    stream.write(&bytes, maxLength: bytes.count)
                }
            }
            // 當前正在處理的回調個數+新的要處理的回調個數
            state.numberOfExecutingStreams += state.streams.count
            // 複製一份
            let localState = state
            // 處理數據
            underlyingQueue.async { localState.streams.forEach { $0(data) } }
        }
    }

    /// 添加有效性判斷回調
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void = { [unowned self] in
            guard self.error == nil, let response = self.response else { return }

            let result = validation(self.request, response)

            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }

            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       withResult: result)
        }

        $validators.write { $0.append(validator) }

        return self
    }

    /// Produces an `InputStream` that receives the `Data` received by the instance.
    ///
    /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`.
    /// Additionally, this method will automatically call `resume()` on the instance, regardless of whether or
    /// not the creating session has `startRequestsImmediately` set to `true`.
    ///
    /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`.
    ///
    /// - Returns: The `InputStream` bound to the internal `OutboundStream`.
    /// 變換成InputStream供外部操做, 調用該方法後會直接發送請求, 返回給上層的InputStream在讀取數據以前必須先open(), 結束時候必須close()
    public func asInputStream(bufferSize: Int = 1024) -> InputStream? {
        // 建立完IOStream以後就會馬上發送請求, 無視是否當即發送請求的控制參數
        defer { resume() }

        var inputStream: InputStream?
        $streamMutableState.write { state in
            // 建立一對IOStream, OutputStream往InputStream寫, buffer默認1k
            Foundation.Stream.getBoundStreams(withBufferSize: bufferSize,
                                              inputStream: &inputStream,
                                              outputStream: &state.outputStream)
            // 打開OutputStream, 準備讀取數據
            state.outputStream?.open()
        }

        return inputStream
    }
    /// 執行一個能夠拋出錯誤的回調, 捕捉異常, 取消請求並拋出錯誤, (在ResponseSerialization中調用)
    func capturingError(from closure: () throws -> Void) {
        do {
            try closure()
        } catch {
            self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
            cancel()
        }
    }
    /// 添加數據流完成回調與隊列
    func appendStreamCompletion<Success, Failure>(on queue: DispatchQueue, stream: @escaping Handler<Success, Failure>) {
        // 先添加一個解析回調
        appendResponseSerializer {
            // 回調內容爲在內部隊列執行添加一個解析完成的回調
            self.underlyingQueue.async {
                self.responseSerializerDidComplete {
                    // 解析完成的回調內容爲操做$streamMutableState
                    self.$streamMutableState.write { state in
                        guard state.numberOfExecutingStreams == 0 else {
                            //若是還有流在處理數據, 就把完成回調追加到數組裏
                            state.enqueuedCompletionEvents.append {
                                self.enqueueCompletion(on: queue, stream: stream)
                            }

                            return
                        }
                        // 不然直接執行回調
                        self.enqueueCompletion(on: queue, stream: stream)
                    }
                }
            }
        }
        //這樣操做能夠保證完成回調在其餘全部的解析器處理完成後添加
    }
    //發送完成事件
    func enqueueCompletion<Success, Failure>(on queue: DispatchQueue, stream: @escaping Handler<Success, Failure>) {
        queue.async {
            do {
                //建立完成事件
                let completion = Completion(request: self.request,
                                            response: self.response,
                                            metrics: self.metrics,
                                            error: self.error)
                //走你
                try stream(.init(event: .complete(completion), token: .init(self)))
            } catch {
                
                // 忽略錯誤, 完成錯誤沒法被處理, 數據已經完整了
            }
        }
    }
}
複製代碼

而後擴展了DataStreamRequest.Stream結構體來快速取得內部數據:markdown

/// 原理都同樣
extension DataStreamRequest.Stream {
    public var result: Result<Success, Failure>? {
        guard case let .stream(result) = event else { return nil }
        return result
    }

    public var value: Success? {
        guard case let .success(value) = result else { return nil }
        return value
    }

    public var error: Failure? {
        guard case let .failure(error) = result else { return nil }
        return error
    }

    public var completion: DataStreamRequest.Completion? {
        guard case let .complete(completion) = event else { return nil }
        return completion
    }
}
複製代碼

DownloadRequest

DownloadRequest用來處理下載任務, 將文件以文件形式保存在磁盤上時使用這個。session

文件處理:

處理本地文件保存策略, 以及本地文件保存路徑閉包

/// 先定義了一個符合OptionSet協議的結構體來決定本地文件的保存策略:
    public struct Options: OptionSet {
        /// 是否建立中間目錄
        public static let createIntermediateDirectories = Options(rawValue: 1 << 0)
        /// 下載文件前是否先移除舊文件
        public static let removePreviousFile = Options(rawValue: 1 << 1)

        public let rawValue: Int

        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }

    // MARK: 下載目標路徑
    
    /// 下載任務會先把文件下載到臨時的緩存路徑, 而後將文件拷貝到下載目標路徑, 使用閉包來決定能夠把下載路徑的決定推遲到下載完成時, 能夠根據臨時文件目錄與下載響應頭來決定下載目標路徑以及文件保存策略
    /// 注意: 若是是下載本地文件(url爲file://開頭的), 回調不會有response
    public typealias Destination = (_ temporaryURL: URL,
                                    _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)

    /// 建立默認建議的下載路徑回調(document根目錄)
    public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory, in domain: FileManager.SearchPathDomainMask = .userDomainMask, options: Options = []) -> Destination {
        { temporaryURL, response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
            let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL

            return (url, options)
        }
    }

    /// 默認的下載路徑回調(處理見下面方法)
    static let defaultDestination: Destination = { url, _ in
        (defaultDestinationURL(url), [])
    }

    /// 返回默認的文件儲存路徑(只是把默認的文件名重命名爲加上Alamofire_前綴, 文件仍是在緩存目錄, 會被系統刪除, 若是要保存文件, 須要轉移到其餘目錄)
    static let defaultDestinationURL: (URL) -> URL = { url in
        let filename = "Alamofire_\(url.lastPathComponent)"
        let destination = url.deletingLastPathComponent().appendingPathComponent(filename)

        return destination
    }
複製代碼

Downloadable下載源

/// 定義了下載請求的下載源
    public enum Downloadable {
        /// 從URLRequest下載
        case request(URLRequestConvertible)
        /// 斷點續傳
        case resumeData(Data)
    }
複製代碼

線程安全的可變狀態

/// 私有包裹一下
    private struct DownloadRequestMutableState {
        /// 支持斷點續傳的任務被取消時, 須要被處理的已下載數據
        var resumeData: Data?
        /// 下載完成後的文件保存路徑
        var fileURL: URL?
    }

    /// 私有線程安全的屬性
    @Protected
    private var mutableDownloadState = DownloadRequestMutableState()

    /// 開放給外部獲取
    public var resumeData: Data? { mutableDownloadState.resumeData }
    public var fileURL: URL? { mutableDownloadState.fileURL }
複製代碼

初始化與兩個初始化時須要肯定的參數

/// 下載源
    public let downloadable: Downloadable
    /// 下載路徑回調
    let destination: Destination

    /// 初始化
    init(id: UUID = UUID(), downloadable: Downloadable, underlyingQueue: DispatchQueue, serializationQueue: DispatchQueue, eventMonitor: EventMonitor?, interceptor: RequestInterceptor?, delegate: RequestDelegate, destination: @escaping Destination) {
        self.downloadable = downloadable
        self.destination = destination

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    //重試時清除數據
    override func reset() {
        super.reset()

        $mutableDownloadState.write {
            $0.resumeData = nil
            $0.fileURL = nil
        }
    }
複製代碼

下載與取消的處理

/// URLSession下載完成/失敗時, 回調過來更新狀態
    func didFinishDownloading(using task: URLSessionTask, with result: Result<URL, AFError>) {
        eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result)

        switch result {
        case let .success(url): mutableDownloadState.fileURL = url
        case let .failure(error): self.error = error
        }
    }

    /// URLSession下載時, 回調更新進度
    func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        downloadProgress.totalUnitCount = totalBytesExpectedToWrite
        downloadProgress.completedUnitCount += bytesWritten

        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }

    ///建立URLSessionTask
    /// 新文件下載
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        session.downloadTask(with: request)
    }
    /// 斷點續傳
    public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask {
        session.downloadTask(withResumeData: data)
    }

    /// 1. 直接取消請求, 不設置resumeData屬性給監聽器, 沒有回調處理已下載數據
    @discardableResult
    override public func cancel() -> Self {
        cancel(producingResumeData: false)
    }

    /// 2. 取消下載, 並判斷是否須要填充resumeData屬性給監聽器, 沒有回調處理已下載數據
    @discardableResult
    public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self {
        cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil)
    }

    /// 3. 取消下載, 用一個回調來處理已下載數據, 會先填充resumeData屬性, 通知監聽器後調用回調
    @discardableResult
    public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self {
        cancel(optionallyProducingResumeData: completionHandler)
    }

    /// 上面1, 2, 3的整合
    private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self {
        // 線程安全
        $mutableState.write { mutableState in
            // 先判斷可否取消
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
            // 更新狀態
            mutableState.state = .cancelled
            // 先告知本身取消被調用了, 父類會告知監聽器
            underlyingQueue.async { self.didCancel() }

            guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else {
                // 若是下載完成的話, 走finish邏輯
                underlyingQueue.async { self.finish() }
                return
            }

            if let completionHandler = completionHandler {
                // 取消時有處理已下載數據的回調
                // 先恢復一下確保請求指標被獲取到了
                task.resume()
                task.cancel { resumeData in
                    // 填入resumeData屬性
                    self.mutableDownloadState.resumeData = resumeData
                    // 告知本身取消成功, 父類會告知監聽器
                    self.underlyingQueue.async { self.didCancelTask(task) }
                    // 執行已下載數據處理回調
                    completionHandler(resumeData)
                }
            } else {
                // 沒有處理下載數據的回調
                task.resume()
                //直接取消
                task.cancel(byProducingResumeData: { _ in })
                //告知
                self.underlyingQueue.async { self.didCancelTask(task) }
            }
        }

        return self
    }

    /// 響應的有效性判斷
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void = { [unowned self] in
            guard self.error == nil, let response = self.response else { return }

            let result = validation(self.request, response, self.fileURL)

            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }

            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       fileURL: self.fileURL,
                                       withResult: result)
        }

        $validators.write { $0.append(validator) }

        return self
    }
複製代碼

UploadRequest

用來建立上傳請求, 能夠從內存(Data), 磁盤(file), 流(InputStream)上傳數據, 注意上傳請求是DataRequest的子類app

上傳源

public enum Uploadable {
        /// 從內存
        case data(Data)
        /// 從文件上傳, 並能夠設置上傳完成後是否刪除源文件
        case file(URL, shouldRemove: Bool)
        /// 從流上傳
        case stream(InputStream)
    }
複製代碼

初始化與初始狀態屬性

/// 用來建立上傳源的協議
    public let upload: UploadableConvertible

    /// 文件管理器, 用來清除任務, 包括寫在硬盤上的多表單上傳數據
    public let fileManager: FileManager

    /// 上傳源, 開始請求時才建立. 可選是由於, 從上傳源協議對象建立的時候, 是能夠失敗的
    public var uploadable: Uploadable?

    /// 初始化
    init(id: UUID = UUID(), convertible: UploadConvertible, underlyingQueue: DispatchQueue, serializationQueue: DispatchQueue, eventMonitor: EventMonitor?, interceptor: RequestInterceptor?, fileManager: FileManager, delegate: RequestDelegate) {
        upload = convertible
        self.fileManager = fileManager

        super.init(id: id,
                   convertible: convertible,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    /// 告知監聽器 建立上傳源成功(Session中調用)
    func didCreateUploadable(_ uploadable: Uploadable) {
        self.uploadable = uploadable

        eventMonitor?.request(self, didCreateUploadable: uploadable)
    }
    /// 告知監聽器 建立上傳源失敗(Session中調用), 而後走重試或者完成邏輯
    func didFailToCreateUploadable(with error: AFError) {
        self.error = error

        eventMonitor?.request(self, didFailToCreateUploadableWithError: error)

        retryOrFinish(error: error)
    }
    
    /// 用URLRequest跟URLSession建立URLSessionUploadTask(Session中調用)
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        guard let uploadable = uploadable else {
            fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.")
        }

        switch uploadable {
        case let .data(data): return session.uploadTask(with: request, from: data)
        case let .file(url, _): return session.uploadTask(with: request, fromFile: url)
        case .stream: return session.uploadTask(withStreamedRequest: request)
        }
    }

    /// 重試時清空數據, 重試時, 上傳源必須從新建立
    override func reset() {
        // Uploadable must be recreated on every retry.
        uploadable = nil

        super.reset()
    }

    /// 轉換爲InputStream供外部鏈接, 必須使用Uploadable.stream, 不然會拋出異常
    func inputStream() -> InputStream {
        guard let uploadable = uploadable else {
            fatalError("Attempting to access the input stream but the uploadable doesn't exist.")
        }

        guard case let .stream(stream) = uploadable else {
            fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.")
        }

        eventMonitor?.request(self, didProvideInputStream: stream)

        return stream
    }
    
    /// 請求完成時, 清理任務(刪除源文件)
    override public func cleanup() {
        defer { super.cleanup() }

        guard
            let uploadable = self.uploadable,
            case let .file(url, shouldRemove) = uploadable,
            shouldRemove
        else { return }

        try? fileManager.removeItem(at: url)
    }
複製代碼

建立上傳源的協議

/// 定義UploadableConvertible協議用來建立上傳源的協議
public protocol UploadableConvertible {
    func createUploadable() throws -> UploadRequest.Uploadable
}
    
/// 擴展了**UploadRequest.Uploadable**實現UploadableConvertible協議, 直接返回本身(外部就不用本身寫生成上傳源的類, 能夠直接用
extension UploadRequest.Uploadable: UploadableConvertible {
    public func createUploadable() throws -> UploadRequest.Uploadable {
        self
    }
}

/// 組合這倆協議, 初始化用(本身初始化要用UploadableConvertible, 父類初始化要用URLRequestConvertible)
public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {}
複製代碼

總結

  • Request類負責鏈接URLRequest與URLSessionTask, 基類主要是定義了一大堆請求中用到的屬性, 而後定義了請求中各個階段的回調方法(不包括響應解析部分, 響應解析相關的,在Response.swift中), 給外部或者本身調用來通知監聽器, 而後繼續往下執行邏輯(包括重試、完成)
  • 四個子類分別增長了具備各自特點的屬性、狀態、回調
  • 四個子類分別使用協議來隔離了請求源的建立,這樣Request類不用關心上傳源來自何處,上傳請求還使用了協議隔離上傳源的建立, 並使用組合協議來組合請求源於上傳源的協議, 設計很巧妙
  • Request.swift中大部份內容都是Request的建立, 以及各類狀態的處理, Task的發出時機爲:
    1. startImmediately屬性爲true(默認), 那麼在對request調用response***方法時, 會爲request添加了responseSerializers, 當添加第一個responseSerializers後,會自動調用resume方法開始請求
    2. 不然須要本身手動對Request對象主動調用resume方法纔會發出請求

備註

Alamofire的使用很是簡單:less

AF.request("請求地址").responseJSON(completionHandler: {
    resp in
    //處理響應
})
複製代碼

可是其內部處理很是複雜, 從開始處理請求地址,到建立URLRequest,再到建立URLSessionTask,每一步都有不少很細的處理。其中Session負責調度Request, Request跟Response是請求的核心。配合上各類有效性判斷,認證、緩存、重試、錯誤拋出等,光看EventMonitor接口,足足有43個回調方法,貫穿了請求初始到響應解析完成中的全部狀態處理,在看完Response對響應的解析後,我會嘗試畫一下時序圖看能不能理清邏輯,學一下代碼的設計與封裝邏輯,對本身擼代碼時的思惟有很大的提高。

以上所有內容均爲我的理解,若有錯誤,歡迎評論指出,將第一時間修改~~很是感謝~~

相關文章
相關標籤/搜索