zoukankan      html  css  js  c++  java
  • [翻译]理解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将会使你保证你写代码更迅速,减少运行时的错误。

  • 相关阅读:
    A1066 Root of AVL Tree (25 分)
    A1099 Build A Binary Search Tree (30 分)
    A1043 Is It a Binary Search Tree (25 分) ——PA, 24/25, 先记录思路
    A1079; A1090; A1004:一般树遍历
    A1053 Path of Equal Weight (30 分)
    A1086 Tree Traversals Again (25 分)
    A1020 Tree Traversals (25 分)
    A1091 Acute Stroke (30 分)
    A1103 Integer Factorization (30 分)
    A1032 Sharing (25 分)
  • 原文地址:https://www.cnblogs.com/richieyang/p/5149173.html
Copyright © 2011-2022 走看看