美文网首页Swift DeveloperiOS开发iOS Developer
Alamofire 4.5.0源码解析-NetworkReach

Alamofire 4.5.0源码解析-NetworkReach

作者: FlyElephant | 来源:发表于2017-07-18 07:56 被阅读196次

    Alamofire 源码解析基本上按照文件的结构顺序进行分析,之前分析了三篇,本篇主要分析四个文件网络状态(NetworkReachabilityManager),安全协议(ServerTrustPolicyManager),时间轴(Timeline) ,校验(Validation).

    NetworkReachabilityManager

    NetworkReachabilityManager主要用于判断当前网络状态,Alamofire相对于AFNetWorking用了两个不同的枚举来表示:
    <pre><code>` public enum NetworkReachabilityStatus {
    case unknown
    case notReachable
    case reachable(ConnectionType)
    }

    /// Defines the various connection types detected by reachability flags.
    ///
    /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
    /// - wwan:           The connection type is a WWAN connection.
    public enum ConnectionType {
        case ethernetOrWiFi
        case wwan
    }`</code></pre>
    

    初始化代码:
    <pre><code>` public convenience init?(host: String) {
    guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
    self.init(reachability: reachability)
    }

    /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0.
    ///
    /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
    /// status of the device, both IPv4 and IPv6.
    ///
    /// - returns: The new `NetworkReachabilityManager` instance.
    public convenience init?() {
        var address = sockaddr_in()
        address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        address.sin_family = sa_family_t(AF_INET)
    
        guard let reachability = withUnsafePointer(to: &address, { pointer in
            return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
                return SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else { return nil }
    
        self.init(reachability: reachability)
    }
    
    private init(reachability: SCNetworkReachability) {
        self.reachability = reachability
        self.previousFlags = SCNetworkReachabilityFlags()
    }`</code></pre>
    

    代码中的MemoryLayout用来来实现原来sizeof的功能:
    <pre><code>`public enum MemoryLayout<T> {

    /// The contiguous memory footprint of `T`, in bytes.
    ///
    /// A type's size does not include any dynamically allocated or out of line
    /// storage. In particular, `MemoryLayout<T>.size`, when `T` is a class
    /// type, is the same regardless of how many stored properties `T` has.
    ///
    /// When allocating memory for multiple instances of `T` using an unsafe
    /// pointer, use a multiple of the type's stride instead of its size.
    ///
    /// - SeeAlso: `stride`
    public static var size: Int { get }
    
    /// The number of bytes from the start of one instance of `T` to the start of
    /// the next when stored in contiguous memory or in an `Array<T>`.
    ///
    /// This is the same as the number of bytes moved when an `UnsafePointer<T>`
    /// instance is incremented. `T` may have a lower minimal alignment that
    /// trades runtime performance for space efficiency. This value is always
    /// positive.
    public static var stride: Int { get }
    
    /// The default memory alignment of `T`, in bytes.
    ///
    /// Use the `alignment` property for a type when allocating memory using an
    /// unsafe pointer. This value is always positive.
    public static var alignment: Int { get }
    
    /// Returns the contiguous memory footprint of the given instance.
    ///
    /// The result does not include any dynamically allocated or out of line
    /// storage. In particular, pointers and class instances all have the same
    /// contiguous memory footprint, regardless of the size of the referenced
    /// data.
    ///
    /// When you have a type instead of an instance, use the
    /// `MemoryLayout<T>.size` static property instead.
    ///
    ///     let x: Int = 100
    ///
    ///     // Finding the size of a value's type
    ///     let s = MemoryLayout.size(ofValue: x)
    ///     // s == 8
    ///
    ///     // Finding the size of a type directly
    ///     let t = MemoryLayout<Int>.size
    ///     // t == 8
    ///
    /// - Parameter value: A value representative of the type to describe.
    /// - Returns: The size, in bytes, of the given value's type.
    ///
    /// - SeeAlso: `MemoryLayout.size`
    public static func size(ofValue value: T) -> Int
    
    /// Returns the number of bytes from the start of one instance of `T` to the
    /// start of the next when stored in contiguous memory or in an `Array<T>`.
    ///
    /// This is the same as the number of bytes moved when an `UnsafePointer<T>`
    /// instance is incremented. `T` may have a lower minimal alignment that
    /// trades runtime performance for space efficiency. The result is always
    /// positive.
    ///
    /// When you have a type instead of an instance, use the
    /// `MemoryLayout<T>.stride` static property instead.
    ///
    ///     let x: Int = 100
    ///
    ///     // Finding the stride of a value's type
    ///     let s = MemoryLayout.stride(ofValue: x)
    ///     // s == 8
    ///
    ///     // Finding the stride of a type directly
    ///     let t = MemoryLayout<Int>.stride
    ///     // t == 8
    ///
    /// - Parameter value: A value representative of the type to describe.
    /// - Returns: The stride, in bytes, of the given value's type.
    ///
    /// - SeeAlso: `MemoryLayout.stride`
    public static func stride(ofValue value: T) -> Int
    
    /// Returns the default memory alignment of `T`.
    ///
    /// Use a type's alignment when allocating memory using an unsafe pointer.
    ///
    /// When you have a type instead of an instance, use the
    /// `MemoryLayout<T>.stride` static property instead.
    ///
    ///     let x: Int = 100
    ///
    ///     // Finding the alignment of a value's type
    ///     let s = MemoryLayout.alignment(ofValue: x)
    ///     // s == 8
    ///
    ///     // Finding the alignment of a type directly
    ///     let t = MemoryLayout<Int>.alignment
    ///     // t == 8
    ///
    /// - Parameter value: A value representative of the type to describe.
    /// - Returns: The default memory alignment, in bytes, of the given value's
    ///   type. This value is always positive.
    ///
    /// - SeeAlso: `MemoryLayout.alignment`
    public static func alignment(ofValue value: T) -> Int
    

    }`</code></pre>

    网络状态监听和取消监听:

    <pre><code>` public func startListening() -> Bool {
    var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
    context.info = Unmanaged.passUnretained(self).toOpaque()

        let callbackEnabled = SCNetworkReachabilitySetCallback(
            reachability,
            { (_, flags, info) in
                let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
                reachability.notifyListener(flags)
            },
            &context
        )
    
        let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
    
        listenerQueue.async {
            self.previousFlags = SCNetworkReachabilityFlags()
            self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
        }
    
        return callbackEnabled && queueEnabled
    }
    
    /// Stops listening for changes in network reachability status.
    public func stopListening() {
        SCNetworkReachabilitySetCallback(reachability, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
    }
    

    `</code></pre>

    ServerTrustPolicyManager

    ServerTrustPolicyManager验证Https证书是否有效,相对于AFNetWorking多了一个证书注销的枚举:

    performDefaultEvaluation:从客户端信任的CA列表中验证证书是否有效.

    pinCertificates:客户端保存服务端证书的拷贝,首先验证域名/有效期信息,然后验证两个证书是否一致.

    pinPublicKeys:客户端保存服务端证书的拷贝,验证服务端证书的公钥是否一致;

    <pre><code>public enum ServerTrustPolicy { case performDefaultEvaluation(validateHost: Bool) case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) case disableEvaluation case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)</code></pre>

    评估证书是否有效:

    <pre><code>` public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
    var serverTrustIsValid = false

        switch self {
        case let .performDefaultEvaluation(validateHost):
            let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
            SecTrustSetPolicies(serverTrust, policy)
    
            serverTrustIsValid = trustIsValid(serverTrust)
        case let .performRevokedEvaluation(validateHost, revocationFlags):
            let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
            let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
            SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)
    
            serverTrustIsValid = trustIsValid(serverTrust)
        case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
            if validateCertificateChain {
                let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
                SecTrustSetPolicies(serverTrust, policy)
    
                SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
                SecTrustSetAnchorCertificatesOnly(serverTrust, true)
    
                serverTrustIsValid = trustIsValid(serverTrust)
            } else {
                let serverCertificatesDataArray = certificateData(for: serverTrust)
                let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)
    
                outerLoop: for serverCertificateData in serverCertificatesDataArray {
                    for pinnedCertificateData in pinnedCertificatesDataArray {
                        if serverCertificateData == pinnedCertificateData {
                            serverTrustIsValid = true
                            break outerLoop
                        }
                    }
                }
            }
        case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
            var certificateChainEvaluationPassed = true
    
            if validateCertificateChain {
                let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
                SecTrustSetPolicies(serverTrust, policy)
    
                certificateChainEvaluationPassed = trustIsValid(serverTrust)
            }
    
            if certificateChainEvaluationPassed {
                outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] {
                    for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
                        if serverPublicKey.isEqual(pinnedPublicKey) {
                            serverTrustIsValid = true
                            break outerLoop
                        }
                    }
                }
            }
        case .disableEvaluation:
            serverTrustIsValid = true
        case let .customEvaluation(closure):
            serverTrustIsValid = closure(serverTrust, host)
        }
    
        return serverTrustIsValid
    }
    

    `</code></pre>

    Timeline

    相对于AFNetWorking时间轴的概念非常👍,关于网络请求中的开始时间,结束时间,序列化时间,总时间,整个请求到结束的时间都一目了然.
    <pre><code>`

    public struct Timeline {
    /// The time the request was initialized.
    public let requestStartTime: CFAbsoluteTime

    /// The time the first bytes were received from or sent to the server.
    public let initialResponseTime: CFAbsoluteTime
    
    /// The time when the request was completed.
    public let requestCompletedTime: CFAbsoluteTime
    
    /// The time when the response serialization was completed.
    public let serializationCompletedTime: CFAbsoluteTime
    
    /// The time interval in seconds from the time the request started to the initial response from the server.
    public let latency: TimeInterval
    
    /// The time interval in seconds from the time the request started to the time the request completed.
    public let requestDuration: TimeInterval
    
    /// The time interval in seconds from the time the request completed to the time response serialization completed.
    public let serializationDuration: TimeInterval
    
    /// The time interval in seconds from the time the request started to the time response serialization completed.
    public let totalDuration: TimeInterval
    
    /// Creates a new `Timeline` instance with the specified request times.
    ///
    /// - parameter requestStartTime:           The time the request was initialized. Defaults to `0.0`.
    /// - parameter initialResponseTime:        The time the first bytes were received from or sent to the server.
    ///                                         Defaults to `0.0`.
    /// - parameter requestCompletedTime:       The time when the request was completed. Defaults to `0.0`.
    /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
    ///                                         to `0.0`.
    ///
    /// - returns: The new `Timeline` instance.
    public init(
        requestStartTime: CFAbsoluteTime = 0.0,
        initialResponseTime: CFAbsoluteTime = 0.0,
        requestCompletedTime: CFAbsoluteTime = 0.0,
        serializationCompletedTime: CFAbsoluteTime = 0.0)
    {
        self.requestStartTime = requestStartTime
        self.initialResponseTime = initialResponseTime
        self.requestCompletedTime = requestCompletedTime
        self.serializationCompletedTime = serializationCompletedTime
    
        self.latency = initialResponseTime - requestStartTime
        self.requestDuration = requestCompletedTime - requestStartTime
        self.serializationDuration = serializationCompletedTime - requestCompletedTime
        self.totalDuration = serializationCompletedTime - requestStartTime
    }
    

    }

    // MARK: - CustomStringConvertible

    extension Timeline: CustomStringConvertible {
    /// The textual representation used when written to an output stream, which includes the latency, the request
    /// duration and the total duration.
    public var description: String {
    let latency = String(format: "%.3f", self.latency)
    let requestDuration = String(format: "%.3f", self.requestDuration)
    let serializationDuration = String(format: "%.3f", self.serializationDuration)
    let totalDuration = String(format: "%.3f", self.totalDuration)

        // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
        // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
        let timings = [
            "\"Latency\": " + latency + " secs",
            "\"Request Duration\": " + requestDuration + " secs",
            "\"Serialization Duration\": " + serializationDuration + " secs",
            "\"Total Duration\": " + totalDuration + " secs"
        ]
    
        return "Timeline: { " + timings.joined(separator: ", ") + " }"
    }
    

    }

    // MARK: - CustomDebugStringConvertible

    extension Timeline: CustomDebugStringConvertible {
    /// The textual representation used when written to an output stream, which includes the request start time, the
    /// initial response time, the request completed time, the serialization completed time, the latency, the request
    /// duration and the total duration.
    public var debugDescription: String {
    let requestStartTime = String(format: "%.3f", self.requestStartTime)
    let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
    let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
    let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
    let latency = String(format: "%.3f", self.latency)
    let requestDuration = String(format: "%.3f", self.requestDuration)
    let serializationDuration = String(format: "%.3f", self.serializationDuration)
    let totalDuration = String(format: "%.3f", self.totalDuration)

        // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
        // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
        let timings = [
            "\"Request Start Time\": " + requestStartTime,
            "\"Initial Response Time\": " + initialResponseTime,
            "\"Request Completed Time\": " + requestCompletedTime,
            "\"Serialization Completed Time\": " + serializationCompletedTime,
            "\"Latency\": " + latency + " secs",
            "\"Request Duration\": " + requestDuration + " secs",
            "\"Serialization Duration\": " + serializationDuration + " secs",
            "\"Total Duration\": " + totalDuration + " secs"
        ]
    
        return "Timeline: { " + timings.joined(separator: ", ") + " }"
    }
    

    }`</code></pre>

    Validation

    Validation是针对Request进行数据校验扩展,校验状态码,ContentType类型:

    <pre><code>` fileprivate func validate<S: Sequence>(
    statusCode acceptableStatusCodes: S,
    response: HTTPURLResponse)
    -> ValidationResult
    where S.Iterator.Element == Int
    {
    if acceptableStatusCodes.contains(response.statusCode) {
    return .success
    } else {
    let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
    return .failure(AFError.responseValidationFailed(reason: reason))
    }
    }

    // MARK: Content Type
    
    fileprivate func validate<S: Sequence>(
        contentType acceptableContentTypes: S,
        response: HTTPURLResponse,
        data: Data?)
        -> ValidationResult
        where S.Iterator.Element == String
    {
        guard let data = data, data.count > 0 else { return .success }
    
        guard
            let responseContentType = response.mimeType,
            let responseMIMEType = MIMEType(responseContentType)
        else {
            for contentType in acceptableContentTypes {
                if let mimeType = MIMEType(contentType), mimeType.isWildcard {
                    return .success
                }
            }
    
            let error: AFError = {
                let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
                return AFError.responseValidationFailed(reason: reason)
            }()
    
            return .failure(error)
        }
    
        for contentType in acceptableContentTypes {
            if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
                return .success
            }
        }
    
        let error: AFError = {
            let reason: ErrorReason = .unacceptableContentType(
                acceptableContentTypes: Array(acceptableContentTypes),
                responseContentType: responseContentType
            )
    
            return AFError.responseValidationFailed(reason: reason)
        }()
    
        return .failure(error)
    }
    

    }`</code></pre>

    Alamofire源码解析到此结束,Swift水平有限,欢迎多多指正~

    相关文章

      网友评论

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

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