Alamofire 源码学习(三)- 惯用编程技巧

作者: song4 | 来源:发表于2016-07-23 02:01 被阅读318次

    本文讨论 Alamofire 中使用到的一些 Swift 惯用编程技巧。

    单例

    单例模式是一种被广泛运用的设计模式。一个设计正确的单例类必须保证应用的生命周期之内在内存中有且只能有一个该类的实例,这也要求,单例类的实例化方法必须是线程安全的。使用 Swift 有若干种方式可以实现单例类,下面是几种常见的实现方式。

    使用带有明显 Objective-C 风格的 dispatch_once 方式:

    class Singleton {
        public class var sharedInstance: Singleton {
            struct Static {
                static var onceToken: dispatch_once_t = 0
                static var instance: Singleton? = nil
            }
    
            dispatch_once(&Static.onceToken) {
                Static.instance = Singleton()
            }
    
            return Static.instance!
        }
    
        private init() {}
    }
    

    在类常量中嵌套一个 struct 也可以完成任务(这里的 struct 类似于 C 语言中函数的 static 局部变量):

    class Singleton {
        public class var sharedInstance: Singleton {
            struct Static {
                static let instance: Singleton = Singleton()
            }
            return Static.instance
        }
    
        private init() {}
    }
    

    实现单例类的最简洁的方式是使用类常量:

    class Singleton {
        public static let sharedInstance = Singleton();
    
        private init() {}
    }
    

    至于类常量的线程安全性,Swift 官方博客上面的一篇文章告诉我们,类常量的初始化工作实际上是在 dispatch_once 线程中完成的:

    The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.

    Alamofire 正是使用了这种方式实现单例类。稍有不同的是,下面的例子使用了一个匿名闭包来初始化类常量,这让我们在实例化 Manager 对象之前可以做一些额外的准备工作:

    // Alamofire: Manager.swift
    
    public class Manager {
        public static let sharedInstance: Manager = {
            let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
            configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
    
            return Manager(configuration: configuration)
        }()
    }
    

    枚举

    Swift 提供了优雅而且功能强大的枚举类型。在 Swift 的枚举类型中,每一个枚举项均可以携带一个或者一组关联值(Associated Values):

    // Alamofire: NetworkReachabilityManager.swift
    
    public enum NetworkReachabilityStatus {
        case Unknown
        case NotReachable
        case Reachable(ConnectionType)
    }
    

    另外,Swift 也支持具有原始值(Raw Value)的枚举类型,这种枚举类型继承自其他类:

    // Alamofire: Error.swift
    
    public enum Code: Int {
        case InputStreamReadFailed           = -6000
        case OutputStreamWriteFailed         = -6001
        case ContentTypeValidationFailed     = -6002
        case StatusCodeValidationFailed      = -6003
        case DataSerializationFailed         = -6004
        case StringSerializationFailed       = -6005
        case JSONSerializationFailed         = -6006
        case PropertyListSerializationFailed = -6007
    }
    

    保镖模式(Bouncer Pattern)

    保镖模式的原则是尽早发现错误,然后将其拒之门外。在 guard 关键字的帮助下,我们可以写出线性的、逻辑非常直观的函数。

    示例代码:

    // Alamofire: MultipartFormData.swift
    
    public func appendBodyPart(fileURL fileURL: NSURL, name: String, fileName: String, mimeType: String) {
        let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
    
        //============================================================
        //                 Check 1 - is file URL?
        //============================================================
    
        guard fileURL.fileURL else {
            let failureReason = "The file URL does not point to a file URL: \(fileURL)"
            setBodyPartError(code: NSURLErrorBadURL, failureReason: failureReason)
            return
        }
    
        //============================================================
        //              Check 2 - is file URL reachable?
        //============================================================
    
        var isReachable = true
    
        if #available(OSX 10.10, *) {
            isReachable = fileURL.checkPromisedItemIsReachableAndReturnError(nil)
        }
    
        guard isReachable else {
            setBodyPartError(code: NSURLErrorBadURL, failureReason: "The file URL is not reachable: \(fileURL)")
            return
        }
    
        // …
    }
    

    guard 关键字是一个极具争议性的关键字。有人认为 guard 关键字是多余的,因为 guard 能做到的事情,if 也可以做到。本文并不打算介入这一争论。

    尾式闭包

    尾式闭包(trailing closure)特性帮助写出简短易读的代码。当函数的最后一个参数是一个闭包时,我们可以使用下面的语法调用该函数:

    func networkRequest(completion: (response: String) -> Void) {
        // function body goes here
    }
    
    // here's how you call this function with a trailing closure:
    
    networkRequest() { response in
        // trailing closure’s body goes here
    }
    

    class 与 struct

    在 Swift 中,classstruct 两种类型之间最大的区别是,class 是引用类型,而 struct 是值类型。下面的两个例子对此做出了说明。

    class 的例子:

    class PersonClass {
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    let personA = PersonClass(name: "Tom")
    let personB = personA
    personB.name = "Bob"
    
    print(personA.name)     // "Bob"
    print(personB.name)     // "Bob"
    

    struct 的例子:

    struct PersonStruct {
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    let personA = PersonStruct(name: "Tom")
    let personB = personA
    personB.name = "Bob"
    
    print(personA.name)     // "Tom"
    print(personB.name)     // "Bob"
    

    一般而言,需要处理复杂的数据结构时,使用 class;需要封装少量简单的数值而且期望在复制操作中进行值拷贝时,使用 struct

    GCD 与 NSOperation

    GCD (Grand Central Dispatch)和 NSOperation 是 Cocoa 框架提供的两种并发编程技术。GCD 在 C 语言层面实现了并发编程的基础设施,NSOperation 则是对 GCD 的上层封装。想要讲清楚这两个概念需要不小的篇幅,本文不打算展开讨论。具体的细节请参考相关技术文档:

    (未完)

    相关文章

      网友评论

        本文标题:Alamofire 源码学习(三)- 惯用编程技巧

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