美文网首页Swift DeveloperiOS学习开发iOS学习笔记
Alamofire 4.5.0源码解析-SessionManag

Alamofire 4.5.0源码解析-SessionManag

作者: FlyElephant | 来源:发表于2017-07-12 22:04 被阅读74次

    Alamofie中的SessionManager,SessionDelegate和TaskDelegate是网络请求的中枢,SessionManager持有SessionDelegate对象,SessionDelegate对象实现URLSessionDelegate,URLSessionTaskDelegate,URLSessionDataDelegate和URLSessionStreamDelegate协议.

    SessionManager

    �SessionManger初始化,默认将类型为SessionDelegate的对象赋值:
    <pre><code>` public init(
    configuration: URLSessionConfiguration = URLSessionConfiguration.default,
    delegate: SessionDelegate = SessionDelegate(),
    serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
    self.delegate = delegate
    self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
    
    /// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`.
    ///
    /// - parameter session:                  The URL session.
    /// - parameter delegate:                 The delegate of the URL session. Must equal the URL session's delegate.
    /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
    ///                                       challenges. `nil` by default.
    ///
    /// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise.
    public init?(
        session: URLSession,
        delegate: SessionDelegate,
        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
        guard delegate === session.delegate else { return nil }
    
        self.delegate = delegate
        self.session = session
    
        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
    
    private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
        session.serverTrustPolicyManager = serverTrustPolicyManager
    
        delegate.sessionManager = self
    
        delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
            guard let strongSelf = self else { return }
            DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
        }
    }
    

    `</code></pre>

    队列命名相对AFNetWorking有所改变:
    <pre><code>let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)</code></pre>

    上传数据,作者通过创建目录的方式,确保数据不会被两个线程同时操作:
    <pre><code>` open func upload(
    multipartFormData: @escaping (MultipartFormData) -> Void,
    usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
    with urlRequest: URLRequestConvertible,
    encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
    {
    DispatchQueue.global(qos: .utility).async {
    let formData = MultipartFormData()
    multipartFormData(formData)

            var tempFileURL: URL?
    
            do {
                var urlRequestWithContentType = try urlRequest.asURLRequest()
                urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
    
                let isBackgroundSession = self.session.configuration.identifier != nil
    
                if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
                    let data = try formData.encode()
    
                    let encodingResult = MultipartFormDataEncodingResult.success(
                        request: self.upload(data, with: urlRequestWithContentType),
                        streamingFromDisk: false,
                        streamFileURL: nil
                    )
    
                    DispatchQueue.main.async { encodingCompletion?(encodingResult) }
                } else {
                    let fileManager = FileManager.default
                    let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
                    let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
                    let fileName = UUID().uuidString
                    let fileURL = directoryURL.appendingPathComponent(fileName)
    
                    tempFileURL = fileURL
    
                    var directoryError: Error?
    
                    // Create directory inside serial queue to ensure two threads don't do this in parallel
                    self.queue.sync {
                        do {
                            try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
                        } catch {
                            directoryError = error
                        }
                    }
    
                    if let directoryError = directoryError { throw directoryError }
    
                    try formData.writeEncodedData(to: fileURL)
    
                    let upload = self.upload(fileURL, with: urlRequestWithContentType)
    
                    // Cleanup the temp file once the upload is complete
                    upload.delegate.queue.addOperation {
                        do {
                            try FileManager.default.removeItem(at: fileURL)
                        } catch {
                            // No-op
                        }
                    }
    
                    DispatchQueue.main.async {
                        let encodingResult = MultipartFormDataEncodingResult.success(
                            request: upload,
                            streamingFromDisk: true,
                            streamFileURL: fileURL
                        )
    
                        encodingCompletion?(encodingResult)
                    }
                }
            } catch {
                // Cleanup the temp file in the event that the multipart form data encoding failed
                if let tempFileURL = tempFileURL {
                    do {
                        try FileManager.default.removeItem(at: tempFileURL)
                    } catch {
                        // No-op
                    }
                }
    
                DispatchQueue.main.async { encodingCompletion?(.failure(error)) }
            }
        }
    }`</code></pre>
    

    SessionDelegate & TaskDelegate

    SessionDelegate对象实现URLSessionDelegate,URLSessionTaskDelegate,URLSessionDataDelegate和URLSessionStreamDelegate协议.

    <pre><code>open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { if let downloadTaskDidWriteData = downloadTaskDidWriteData { downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { delegate.urlSession( session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite ) } }</code></pre>

    SessionDelegate通过自定义下标,取出TaskDelegate的代码比较有参考意义:
    <pre><code>/// Access the task delegate for the specified task in a thread-safe manner. open subscript(task: URLSessionTask) -> Request? { get { lock.lock() ; defer { lock.unlock() } return requests[task.taskIdentifier] } set { lock.lock() ; defer { lock.unlock() } requests[task.taskIdentifier] = newValue } }</code></pre>

    TaskDelegate的子类DownloadTaskDelegate关于下载进度方法的回调实现:

    <pre><code>` 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) }
            }
        }
    }`</code></pre>
    

    DispatchQueue

    Alamofire对DispatchQueue调用队列设置参数类型的进行简单的扩展:
    <pre><code>`import Dispatch
    import Foundation

    extension DispatchQueue {
    static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
    static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
    static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
    static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }

    func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
        asyncAfter(deadline: .now() + delay, execute: closure)
    }
    

    }`</code></pre>

    MultipartFormData

    MultipartFormData中关于boundaryType的判断和封装方代码:

    <pre><code>`open class MultipartFormData {

    // MARK: - Helper Types
    
    struct EncodingCharacters {
        static let crlf = "\r\n"
    }
    
    struct BoundaryGenerator {
        enum BoundaryType {
            case initial, encapsulated, final
        }
    
        static func randomBoundary() -> String {
            return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random())
        }
    
        static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
            let boundaryText: String
    
            switch boundaryType {
            case .initial:
                boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
            case .encapsulated:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
            case .final:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
            }
    
            return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
        }
    }`</code></pre>
    

    当看到作者append方法中的注释的时候,瞬间对作者肃然起敬,大家自己感受下:

    <pre><code>` public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
    let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)

        //============================================================
        //                 Check 1 - is file URL?
        //============================================================
    
        guard fileURL.isFileURL else {
            setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
            return
        }
    
        //============================================================
        //              Check 2 - is file URL reachable?
        //============================================================
    
        do {
            let isReachable = try fileURL.checkPromisedItemIsReachable()
            guard isReachable else {
                setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
                return
            }
        } catch {
            setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
            return
        }
    
        //============================================================
        //            Check 3 - is file URL a directory?
        //============================================================
    
        var isDirectory: ObjCBool = false
        let path = fileURL.path
    
        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
            setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
            return
        }
    
        //============================================================
        //          Check 4 - can the file size be extracted?
        //============================================================
    
        let bodyContentLength: UInt64
    
        do {
            guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
                setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
                return
            }
    
            bodyContentLength = fileSize.uint64Value
        }
        catch {
            setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
            return
        }
    
        //============================================================
        //       Check 5 - can a stream be created from file URL?
        //============================================================
    
        guard let stream = InputStream(url: fileURL) else {
            setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
            return
        }
    
        append(stream, withLength: bodyContentLength, headers: headers)
    }`</code></pre>
    

    鉴于本人Swift水平有限,如有不当,欢迎大家多多指正~

    相关文章

      网友评论

        本文标题:Alamofire 4.5.0源码解析-SessionManag

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