Swift DeveloperiOS学习开发iOS学习笔记

Alamofire 4.5.0源码解析-SessionManag

2017-07-12  本文已影响74人  FlyElephant

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水平有限,如有不当,欢迎大家多多指正~

上一篇 下一篇

猜你喜欢

热点阅读