[iOS] Swift 初学手册:可选类型 (Optionals)

原文地址:http://blog.callmewhy.com/2014/09/23/beginners-guide-optionals-swift/

几周前 (译者注:原文发表于6月24日),苹果发布了一个全新的编程语言: Swift 。从那时起,我一直在阅读Swift 官方手册,并且在 Xcode6 beta 上把玩学习。我开始喜欢上了 Swift 的简洁和语法。我和我的团队一起学习这门全新的语言,并且将它和 Objective-C 这个有着30年历史的老伙计进行对比。同时,我们也在努力探索如何能让初学者们轻松的掌握 Swift 。

两周前,我们发布了 Swift 基础教程,在接下来的几周里,我们将会写一系列的教程来介绍 Swift 的新特性。这一周,让我们来看看可选类型 (Optionals)。

概述

在前面的教程里我有提及可选类型的概念,但是没有深入讲解。

那么,什么是可选类型?

在 Swift 中,当我们声明一个变量的时候,默认情况下是 非可选类型 (non-optional) ,也就是说,你必须指定一个不为 nil 的值。如果你硬是要把一个非可选类型的变量设为 nil ,那么编译器就会告诉你:“嘿你不能把它设置成 nil 好嘛”。没错就是这样:

var message: String = "Swift is awesome!" // OK
message = nil // compile-time error

当然编译器给出的错误消息可就没这么友善了,一般会显示Could not find an overload for ‘__conversion’ that accepts the supplied arguments 这种错误。同样,在类中声明的变量也是这样,默认情况下是费可选类型的:

class Messenger {
    var message1: String = "Swift is awesome!" // OK
    var message2: String // compile-time error
}

在 message2 处你会得到一个编译错误,因为它没有初始值。对于那些从 Objective-C 一路走来的小伙伴们可能会感觉很意外,在 Objective-C 里这种情况根本就不会有问题好嘛:

NSString *message = @"Objective-C will never die!";
message = nil;

class Messenger {
    NSString *message1 = @"Objective will never die!";
    NSString *message2;
}

不过,你也可以在 Swift 中声明一个没有初始化的变量, Swift 提供了可选类型来表示没有值的情况。只需要在声明的类型后面加上问号 ? 即可:

class Messenger {
    var message1: String = "Swift is awesome!" // OK
    var message2: String? // OK
}

你也可以给可选类型的变量们赋值,如果不赋值那么它的值自动就设为 nil 。

缘由

为什么要这么设计呢?苹果官方给出的解释是,因为 Swift 是一门类型安全的语言。从前面的例子中可以看出, Swift的可选类型会进行编译检查,防止一些常见的运行时错误。让我们看一看下面的例子,这样可以更好地理解。

比如说,在 Objective-C 中有如下代码:

- (NSString *)findStockCode:(NSString *)company {
    if ([company isEqualToString:@"Apple"]) {
        return @"AAPL";
    } else if ([company isEqualToString:@"Google"]) {
        return @"GOOG";
    }

    return nil;
}

在上面的方法里,你可以用 findStockCode 方法来获取到股票的代码,显然只有 Apple 和 Google 的查询会返回值,其他情况都会返回 nil 。

假设我们在下面的代码中会调用这个方法:

NSString *stockCode = [self findStockCode:@"Facebook"]; // nil is returned
NSString *text = @"Stock Code - ";
NSString *message = [text stringByAppendingString:stockCode]; // runtime error
NSLog(@"%@", message);

这段代码在编译时不会有任何问题,但是如果输入的是 Facbook 则会返回 nil ,导致运行时错误。

而在 Swift 里,和运行时错误不用, Swift 会在编译时就提示错误信息,我们可以把上面的代码在 Swift 中重写:

func findStockCode(company: String) -> String? {
   if (company == "Apple") {
      return "AAPL"
   } else if (company == "Google") {
      return "GOOG"
   }

   return nil
}

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode  // compile-time error
println(message)

在上面的代码里, stockCode 被定义成了可选类型,这意味着它可以有一个 string 的值,也可以为 nil 。代码无法通过编译,会提示一个错误:value of optional type String? is not unwrapped

正如你在例子中看到的,Swift 的可选类型加强了 nil 检测,为开发者提供了编译时的检查,合理的使用可选类型可以有效地提高代码质量。

强制解析

慢着慢着,前面说了那么多好处,但是代码还是没通过编译啊!别急,我们需要检测一下 stockCode 是否为 nil ,把代码做如下修改:

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if stockCode {
    let message = text + stockCode!
    println(message)
}

和 Objective-C 中类似,我们先对它进行检测,看看它是不是有值。如果不为 nil ,我们可以在后面加上一个感叹号 ! 进行解析,这样 Xcode 就知道:“嗯我可以使用这个值了”。在 Swift 里我们称之为 强制解析 (forced unwrapping) ,通过感叹号强制获取可选类型的真实值。

再回到上面的代码中。我们只是在强制解析之前,检测了一下看看变量是否为 nil 而已。这和 Objective-C 中常见的 nil 检测也没啥区别啊。如果我忘了检测呢?看下下面的代码:

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode!  // runtime error

这样我们不会得到编译错误,因为我们用了强制解析,编译器已经假定这个可选类型肯定有值。显然这样是错误的,运行的时候会得到如下错误:

fatal error: Can’t unwrap Optional.None

可选绑定

除了强制解析,可选绑定 (optional binding) 是一个更值得推荐的解析方案。 你可以用可选绑定来检测一个可选类型的值有没有值,如果有值则解析出来并存储到一个临时的变量里。

废话少说,放码过来!让我们来看看下面这个使用了可选绑定的示例代码:

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if let tempStockCode = stockCode {
    let message = text + tempStockCode
    println(message)
}

代码中的 if let (或者 if var ) 是可选绑定的两个关键词。翻译成人类语言,大概是这个样子:“如果 stackCode 它有值,把它的值存到 tempStackCode 里,然后继续执行接下来的代码块。如果它没值,跳过后面的代码块。” 因为tempStockCode 是一个新的常量,所以你不再需要添加 ! 后缀。

你可以把方法调用放在 if 里,这样代码看起来更简洁:

let text = "Stock Code - "
if var stockCode = findStockCode("Apple") {
    let message = text + stockCode
    println(message)
}

这里, stockCode 不再是可选类型,我们可以直接使用。如果 findStockCode 方法返回了 nil 则会跳过后面的代码块。

可选链

在解释可选链之前,我们先对原始代码做一些小小的修改。我们创建一个新的类叫做 Stock ,它有 code 和 price 两个可选类型的属性。findStockCode 函数用来返回一个 Stock 对象,而不是一个 String 对象:

class Stock {
    var code: String?
    var price: Double?
}

func findStockCode(company: String) -> Stock? {
    if (company == "Apple") {
        let aapl: Stock = Stock()
        aapl.code = "AAPL"
        aapl.price = 90.32

        return aapl

    } else if (company == "Google") {
        let goog: Stock = Stock()
        goog.code = "GOOG"
        goog.price = 556.36

        return goog
    }

    return nil
}

接下来,我们先用 findStockCode 函数查找股票的代码,然后计算购买100股所需要的总价:

if let stock = findStockCode("Apple") {
    if let sharePrice = stock.price {
        let totalCost = sharePrice * 100
        println(totalCost)
    }
}

函数的返回值是可选类型,我们通过可选绑定来检测是否有值,显然股票的价格也是一个可选类型,于是我们继续使用if let 来检测它是否有值。

上面的代码没有任何问题,不过这一层一层的 if 嵌套实在是太麻烦了,如果可选类型层次多点,很可能形成下面的情况:

if let x = xxx() {
    if let x = xxx() {
        if let x = xxx() {
            if let x = xxx() {
                if let x = xxx() {
                    if let x = xxx() {
                        if let x = xxx() {
                            if let x = xxx() {
                                if let x = xxx() {
                                    if let x = xxx() {
                                        if let x = xxx() {
                                            if let x = xxx() {

                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

呃上面这段代码是我自己瞎写的,原文中并没有嗯。

除了使用 if let ,我们可以通过可选链来简化代码。我们可以用问号将多个可选类型串联起来:

if let sharePrice = findStockCode("Apple")?.price {
    let totalCost = sharePrice * 100
    println(totalCost)
}

可选链提供了访问变量的另一种方式,代码现在看上去也更加的干净整洁。上面只是一个基础的使用,更加深入的学习可以参考官方文档

Swift 和 Objective-C 的交互

Swift 中的可选类型十分强大,尽管可能一开始的时候需要花点时间慢慢熟悉。可选类型让你的代码更清晰,而且可以避免一些 nil 引起的问题。

Swift 有设计与 Objective-C 交互的 API,当我们需要和 UIKit 或者其他框架的 API 交互的时候,你肯定会遇到可选类型。下面列举一些 UITableView 中可能会遇到的可选类型:

func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
    // Return the number of sections.
    return 1
}

func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
    // Return the number of rows in the section.
    return recipes.count
}

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

    cell.textLabel.text = recipes[indexPath.row]

    return cell
}

总结

对于一名开发者来说,理解可选类型的工作原理是十分必要的。这也就是为什么我们专门写一篇文章来介绍可选类型。它可以帮助开发者在编译阶段就发现隐藏的问题,从而避免运行时错误。当你习惯了这种语法,你将会愈发欣赏可选类型的魅力所在。

享受这个美好的世界吧。真棒。(没错这句也是我乱加的)



原文地址

时间: 2024-11-06 23:01:21

[iOS] Swift 初学手册:可选类型 (Optionals)的相关文章

swift中的可选类型

前几天转了一篇介绍?和感叹号的文章,现在自己搞明白了,记录一下! 1.swift中的可选类型变量表示这个变量可能有值,也可能为空.你可能会想这个直接用一个指针不就搞定了么?但swift是不支持指针的.(这个解释有些牵强,但是在给一个普通变量赋值为nil的时候,会出现语法错误) . 2.问号和叹号都能声明swift中的可选类型变量,比如: var num : Int? = 10 var num1 : Int! = 11 3.上边的两中声明方式是有区别的 问号声明的变量 在使用的时候必须强制解包赋值

[Swift]学习笔记-可选类型/可选链

可选类型/可选链                        Make-by-LJW ---转载请注明出处... 它的可选性体现于请求或调用的目标当前可能为空(nil) 如果可选的目标有值,那么调用就会成功: 如果选择的目标为空(nil),则这种调用将返回空(nil) 多次调用被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效. 因为可选链的结果可能为nil,可能有值.因此它的返回值是一个可选类型. 可以通过判断返回是否有值来判断是否调用成功 有值,说明调用成功 为nil,

【Swift语言】可选类型

Swift定义后缀?来作为标准库中的定义的命名型类型Optional<T>的简写. 类型Optional<T>是一个枚举,有两种形式,None和Some(T),又来代表可能出现或可能不出现的值.任意类型都可以被显式的声明(或隐式的转换)为可选类型.当声明一个可选类型时,确保使用括号给?提供合适的作用范围.

关于swift中的可选类型

Optional 可选类型 Optional 是 Swift 的一大特色,也是 Swift 初学者最容易困惑的问题 定义变量时,如果指定是可选的,表示该变量可以有一个指定类型的值,也可以是 nil 定义变量时,在类型后面添加一个 ?,表示该变量是可选的 变量可选项的默认值是 nil 常量可选项没有默认值,主要用于在构造函数中给常量设置初始数值 //: num 可以是一个整数,也可以是 nil,注意如果为 nil,不能参与计算 let num: Int? = 10 如果 Optional 值是 n

Swift(二,元组,可选类型,类型转化)

一,首先,元组是Swift中特有的,OC中没有元组相关类型,具体怎么用,看下面的例子吧 //1.使用元组来定义一组数据 let infoTuple = ("cjh",18,1.8) let nameTuple = infoTuple.0 print(nameTuple) let count = nameTuple.characters.count //2.1 普通元组的定义方式 let errorMsg = ("error",123) errorMsg.0 //2.

Swift学习-----可选类型

可选类型 * 可选类型表示变量可以有值, 也可以没有值 * C 和 Objective-C 中并没有可选类型这个概念 * Swift中只有可选类型才可以赋值为nil * 如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为nil * 格式: Optional<类型> 或 在类型后面加上?号 可选类型的取值是一个枚举 * None 没有值 * Some 有值 * 由于可选类型在Swift中随处可见, 所以系统做了一个语法糖, 在类型后面加上? 注意: * nil不能用于非可选的常量和变

Swift中的Optional类型 (可选类型)与强制解包 ? !

我们在swift的开发中会经常遇见?和! ,理解这两个符号深层次的内容对我们的开发是相当有利的: 目前网上对swift3.0的教程还相当的少,如果去搜索会发现早期的说法,在定义变量的时候,swift是不会给变量赋值初始值的,所以当你声明一个字符串变量的时候,var string:String   就会被系统报错,但是目前的swift版本却不报错了. 但是你要给一个字符串赋值未nil的时候,var string:String = nil 就一定是错误的   因为nil并不是一个字符串类型,而实际上

初步swift语言学习笔记2(可选类型?和隐式可选类型!)

作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/28904115 转载请注明出处 假设认为文章对你有所帮助.请通过留言或关注微信公众帐号fengsh998来支持我,谢谢. 可选类型.隐式可选类型 在swift中.可选类型其根源是一个枚举型.里面有None和Some两种类型.事实上所谓的nil就是Optional.None, 非nil就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这

[swift]可选类型

可选类型 <Swift权威指南>第2章千里之行始于足下——Swift语言基础,本章挑选了Swift语言的最基本特性加以介绍.尽管这些特性只占Swift全部特性的很少一部分,但却是所有的Swift程序都必不可少的.所以,读者通过对本章学习,可以使用Swift编写最基本的程序,并对Swift提供的新特性深深地震撼.本节为大家介绍可选类型. AD: 2.6  可选类型 可选类型也是Swift语言新添加的对象.主要是为了解决对象变量或常量为空的情况.在前面定义的变量和常量都不能为空.里面必须要有值.