• [翻译]理解Swift中的Optional


    原文出处:Understanding Optionals in Swift

    苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便、更安全。然而,一个很有力的特性Optional,在你第一次使用时可能会感到困惑。Optionals将会在编译阶段检查哪些值为nil。通过这种方式,你可以更好的保证应用程序交付在用户手里是可运行的。在Swift中,Optionals也提供了一些接口用来和遗留的Objective-C代码之间交互。

    初试Optional

    让我们在XCode中新建一个叫做swift-optionals的playground文件。你可以添加下面的代码来看看Optionals是什么样的。

    import Foundation
    
    var rightURL = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
    var wrongURL = NSURL(string: "this is not a real url") // => nil

    在这种场景下,我们试着通过字符串来创建NSURL。对于rightURL,我们得到了一个Some的结果,里面存放着一个NSURL对象,对于wrongURL,我们将得到nil。在两种情况下都没有直接得到NSURL,这是一件好事,因为Some结果需要显示的解包,这将迫使我们检查nil值,让我们看看这是如何工作的。

    一切都是Some

    如果我们在XCode中看下NSURL的构造函数,我们将会看到如下的代码:

    convenience init?(string URLString: String)
    

    那个在init之后的问号标记告诉我们当构造函数执行完毕,NSURL将会返回一个Optional。在Swift中,Optional是一个实际的类型,更加明确的说是一个泛型的enum,这种类型或者持有另一个对象,或者是nil。让我们看看Optional长什么样?

    enum Optional<T> : Reflectable, NilLiteralConvertible {
        case None
        case Some(T)
    
        /// Construct a `nil` instance.
        init()
    
        /// Construct a non- `nil` instance that stores `some`.
        init(_ some: T)
    ...
    

    我们能够看到Optional枚举包括两个case,None代表nil,Some代表具体的泛型类型。在Swift中,我们可以把任何东西装箱在Optional.Some中。

    Optional<String>.Some("really good stuff") // => {Some "really good stuff"}
    

    另一方面,None等价于nil

    Optional<String>.None == nil // => true
    

    从这个角度来讲,你可以把Optional理解为一个箱子,有可能会包含一个具体的类型,也有可能没有包含。向一个方法或者函数发送一个箱子类型而不是具体类型,编译器将会强迫你打开箱子检查箱子里面的类型。如果箱子是空的,你可以捕捉这个错误并且处理这种错误。

    Swift将会推断出变量的类型,不过为了让事情变得更透明,我们显示的以两种方式来使用Optional:

    import Foundation
    
    var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
    var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil
    

    对于rightURL,我们通过一种冗长的方式来创建一个Optional类型,因为我们会大量使用Optional,Swift给了我们一种简写方式,通过在变量后面追加一个问号‘?',就如同我们之前看到的NSURL构造器那样。但是如果我们在NSURL变量后面去掉问号会怎么样?

    import Foundation
    
    var cheatURL: NSURL = NSURL(string: "http://www.reactive.io")
    

    我们将会得到一个编译错误”Value of optional type ‘NSURL?’not unwrapped; did you mean to use ‘!’or ‘?’”。我们已经知道'?'是干嘛的了,那么感叹号标记'!'是干嘛的呢?

    隐式和显示Optional

    在一个类型后面使用'?'用来显示表明这是一个Optional,让我们使用'!'看看会发生什么:

    import Foundation
    
    var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io/tips") // => http://www.reactive.io/tips
    

    这次我们得到了一个没有被Some包装的NSURL。使用'!'使得implicitURL看起来跟rightURL这个optional差不多,用'!'标记的类型实为ImplicitlyUnwrappedOptional,当你使用值的时候Swift编译器将会自动为你展开里面的值。使用ImplicitlyUnwrappedOptional类型会带来危险,因为编译器不会迫使我们处理值为nil的情况。但是初始化为nil会帮我们跟遗留的Objective-C代码之间搭起桥梁。

    下面是4中不同的Optional使用方式:

    import Foundation
    
    var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
    var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil
    
    var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io") // => http://www.reactive.io/tips
    var explicitURL: ImplicitlyUnwrappedOptional<NSURL> = NSURL(string: "this is another bad url") // => nil
    

    如何使用Optional

    第一种方法是显示检查Optional是否是nil:

    if rightURL != nil {
        println("got a URL: (rightURL)") // => "got a URL: Optional(http://www.reactive.io)"
    }
    else {
        println("no URL, sorry :(")
    }
    

    在上面得到了Optional(http://www.reactive.io),这并不是我们想要的,我们想得到的是Optional里面包含的内容。如何做到呢?靠'!'符号,在变量或常量后面追加'!'符号将会展开Optional里面的值,如果是nil值将会抛出异常,让我们试试:

    if rightURL != nil {
        println("got a URL: (rightURL!)") // => "got a URL: http://www.reactive.io"
    }
    else {
        println("no URL, sorry :(")
    }
    

    我们可以通过另一种方式if let块来实现:

    if let url = rightURL {
        println("got a URL: (url)") // => "got a URL: http://www.reactive.io"
    }
    else {
        println("no URL, sorry :(")
    }
    

    你也可以通过switch语句来实现:

    switch rightURL {
    case nil:
        println("no URL, sorry :(")
    default:
        println("got a URL: (rightURL!)")
    }
    

    你还可以使用??操作符进行链式调用得到不为nil的值:

    var myURL = wrongURL ?? explicitURL ?? rightURL // {Some http://www.reactive.io}
    println("got a URL: (myURL!)") // => "got a URL: http://www.reactive.io"
    

    编写一个类

    是时候来看看如何在面向对象的代码中使用Optional类型了,复制下面的代码到playground中:

    import Foundation
    
    class Person {
        var name: String
        var address: String
    
        init(name: String, address: String) {
            self.name = name
            self.address = address
        }
    
        func description() -> String {
            return "(name) @ (address)"
        }
    }
    
    class Box {
        var contents: String
        var sender: Person!
        var recipient: Person?
    
        init(contents: String) {
            self.contents = contents
        }
    }
    
    var alice = Person(name: "Alice", address: "New York City")
    var book  = Box(contents: "A Good Book")
    
    book.sender = alice // => {name "Alice" address "New York City"}

    注意在Box类中,sender属性是一个ImplicitlyUnwrappedOptional类型,recipient属性是Optional类型。不过若是将这两个类型换为普通的Person类型,Swift编译器将会报出一个错误。因为这两个属性并没有在构造函数中赋值,所以这两个属性在构造函数调用的时候没有被初始化。在上面的例子中,book被初始化后,sender和recipient都默认为nil,但是我们确定book一定有一个sender,所以sender为ImplicitlyUnwrappedOptional类型,在例子中,sender为alice.但是不一定有recipient,所以别人在使用book对象的时候需要检查recipient是否有值。

    方法调用

    如果我们想要得到sender或者recipient的description,我们也许会得到一些麻烦。这是因为我们不能在nil值上调用description方法,另外使用!强制展开nil值Optional还会抛出异常。使用if else条件表达式调用方法会显得很繁琐。Swift提供了另一个工具,通过使用?操作符来进行链式调用。在调用方法的时候先检查值是否是nil:

    book.sender?.description() // => {Some "Alice @ New York City"}
    book.recipient?.description() // => nil

    我们使用了一种真确的方法调用了description方法,更进一步我们得到了一个Optional类型:

    book.recipient = Person(name: "Bob", address: "San Francisco")
    
    if let note = book.recipient?.description() {
        println("Hey (note), enjoy the Book!") // => "Hey Bob @ San Francisco, enjoy the Book!"
    }

    总结

    本文说了一些关于Swift中Optionals的事情,这将帮助你在写代码的时候更好的用上它,并且在使用类库的时候使用它们。熟练的使用不同方式的Optional将会使你保证你写代码更迅速,减少运行时的错误。

  • 相关阅读:
    js笔记一:在HTML中使用javascript
    opencv排错总结
    《学习openCV》例程解析 ex_8_2 (轮廓)
    《学习openCV》例程解析 ex_9_3(codeBook模型实现背景减除)
    《学习openCV》例程解析 ex_9_1 (像素片段)
    《学习openCV》例程解析 ex_9_2(背景减除)
    《学习openCV》例程解析 ex_8_3 (轮廓)
    JavaScript鼠标事件
    sumlime text2 快捷键
    HTML字符实体(转)
  • 原文地址:https://www.cnblogs.com/richieyang/p/5149173.html
Copyright © 2020-2023  润新知