在一个值可能缺失的情况下,你可以使用可选类型。一个可选类型代表两种可能:要么有一个值,并且你可以展开获取该可选类型的值。要么根本没有值。
可选的概念在C和Objective-C语言中是不存在。在Objective-C中最类似可选类型的功能是一个方法将返回nil或者一个对象,返回nil意味着“缺失一个有效的对象”。然而,这只适合于对象,不适合结构体,基本的数据类型,或者枚举值。对于这些类型情况,Objective-C中的方法会返回一个特殊值(例如NSNotFound)来表示缺失一个值。这类似于假定方法调用者知道该方法返回值有一个特殊的值需要检测。Swift的可选类型允许你不需要指定常量,可以为所有任何类型表示一个缺失值。
下面一个例子,说明如何使用可选类型来解决值缺失的问题。Swift中的Int类型有一个初始化方法:转化一个字符串为整数。但是,不是每个字符串都可以被转化为整数,例如,字符串“123”可以被转化为整数123,但是“Hello,world”没有一个明显的数字可以被转化。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
因为初始化方法可能会转化失败,所以它会返回一个可选类型的Int,而不是Int,写作Int?。问号表示值是可选的,意味着它可能包含了一些整数值,或者它可能根本没有值。
nil 如果你想设置一个可选类型的变量为没有值状态,那么可以通过赋给它一个特殊的值nil:
var serverResponseCode: Int? = 404
serverResponseCode = nil
nil不能被用作非可选类型的常量或者变量。如果代码中需要一个常量或者变量在特定条件下有缺失值的情况,那么就要总是声明该变量或者常量为可选类型。如果你定义了一个可选的变量,并且没有设置缺省值,该变量会自动被设置为默认值nil:
var surveyAnswer: String? // surveyAnswer is automatically set to nil
Swift中的nil与Objective-C中的ni不同,在Objective-C中,nil是指向一个不存在的对象的指针,而在Swift中,nil不是一个指针—它是代表一个确定类型的值的缺失。任何可选类型 都可以被设置为nil,不仅仅是对象类型。
If 条件语句 和 强制展开
你可以使用If语句通过与nil的比较来判断是否一个可选类型包含一个值。使用“==”或者“!=”操作符来实现。 如果一个可选类型有一个值,那么它被认为是“不等于”nil: if convertedNumber != nil { print(“convertedNumber contains some integer value.”) }
一旦你能够确定某个可选 是包含一个值,那么你可以通过在可选类型的名字后面添加一个叹号(!)来获取它里面的值。这个叹号有效的说明:“我确切的知道这个可选已经有一个值;请使用该值。”这种方式被看作是 可选类型值的强制展开:
if convertedNumber != nil { print(“convertedNumber has an integer value of (convertedNumber!).”) }
如果使用!来获取一个不存在值的可选类型,那么会导致运行时错误发生。在使用叹号来强制展开可选值之前,总是要确定该可选是包含一个非空的值。
可选绑定 使用可选绑定来确定一个可选是否包含一个值,如果包含,使该值作为一个临时常量或者变量。可选绑定可以使用if和while语句来检查一个可选类型的值,并且取出这个值作为一个常量或者变量,如下所示,为一个if语句编写一个可选绑定:
if let constantName = someOptional { statements }
下面我们可以重写下 possibleNumber 的例子,使用可选绑定,而不是强制展开:
if let actualNumber = Int(possibleNumber) { print(""(possibleNumber)" has an integer value of (actualNumber)") } else { print(""(possibleNumber)" could not be converted to an integer") } // Prints ""123" has an integer value of 123”
这段代码可以被解释为:“如果Int(possibleNumber)返回的可选 Int包含一个值,那么把这个值设置给一个新的常量actualNumber”
如果转化成功,那么在第一个if条件语句中actualNumber 常量变为可用,它已经被可选类型中所包含的值初始化了,因此这里就不再需要添加后缀!来获取它的值。常量和变量都可以用在可选绑定。如果你想要在if语句的第一个分支条件中操作actualNumber的值,那么你可以用 if var actualNumber来替换,该可选所包含的值将作为一个变量而不是一个常量使用。
如果需要,一个if语句中可以包含多个可选绑定和布尔条件,使用逗号隔开。如果其中任何一个可选绑定的值为nil或者任何一个布尔值等于false,那么整个if条件语句被认为是false。如下:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 { print("(firstNumber) < (secondNumber) < 100") } // Prints "4 < 42 < 100"
等同于
if let firstNumber = Int("4") { if let secondNumber = Int("42") { if firstNumber < secondNumber && secondNumber < 100 { print("(firstNumber) < (secondNumber) < 100") } } } // Prints "4 < 42 < 100”
在if语句中用可选绑定创建的常量或者变量只在该if语句中可用,与之不同,在guard语句中创建的常量或者变量在guard下面的代码中都是可用的。
隐式展开的可选类型
如上面所说,可选表示了一个常量或者变量是允许存在“没有值”。可选的类型 可以用一个if语句来检查是否存在一个值,并且可以使用可选绑定来获取它的值,如果可选包含一个值的话。有时候,一个可选类型在第一次被设置了初始化值之后,在整个程序结构中,该可选总是清晰明确的有一个值。在这种情况下,就不需要每次都检查和展开该可选的值。它可以被直接获取,因为它一直都有值,是安全的。这类可选类型被称为隐式展开的可选类型。编写一个隐式展开的可选类型是通过在可选类型后面添加一个叹号(String!)而不是一个问号(String?)。隐式展开的可选类型也是一种普通的可选类型,但是,它可以像一个非可选类型值被使用,不需要每次都展开它的可选值。下面的例子展示了一个可选字符串与隐式展开的可选字符串之间在获取值上的不同:
let possibleString: String? = "An optional string." let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string." let implicitString: String = assumedString // no need for an exclamation mark”
你可以把一个隐式展开的可选类型看作为在使用时有权限自动展开它的值,而不需要每次使用时在可选后面再添加一个叹号。声明的时候在可选类型名称后面直接添加一个叹号。你也可以把一个隐式展开的可选看作为一个普通的可选,可以检查是否它包含一个值: if assumedString != nil { print(assumedString) } // Prints "An implicitly unwrapped optional string.”
你还可以对一个一个隐式展开的可选使用可选绑定,检查和展开它的值:
if let definiteString = assumedString { print(definiteString) } // Prints "An implicitly unwrapped optional string.”
不要对一个后续可能变为nil的变量使用隐式展开的可选类型,当后续需要检查一个变量是否为nil的时候,总是声明该变量为可选类型。