某一些情况下,通过某一个值可能不存在,那么需要使用可选类型。一个可选类型存在2种可能性:
1有值,可以通过解包访问这个值;
2没有值
下面有一个例子可以表明怎么使用可选类型去处理一个可能没有值的情况。Swift的Int类型有提供了构造器,这个构造器可以把String类型的值初始化生成一个整型数。String "123"可以被转换成整型数123,而"Hello,world"就不知道可以转换成什么数字了,因此会转换失败返回空,下面是尝试使用该构造器把一个String转换成整型:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推断为类型 Int?
因为这个构造器可能会失败,它的返回值的类型是可选的Int类型(Int?),而不是整型Int。这个问号表明它包含的值是可选的,也就是说可能会包含一个Int的值,也可能不包含任何值。当然也不会其他类型的值,肯定是一个Int值或者没有值。
nil
一个可选的变量要怎么表示它没有值的状态呢?把它赋值为nil:
var serverResponseCode: Int? = 404
serverResponseCode = nil // serverResponseCode now contains no value
NOTE:非可选的常量或者变量不可以使用nil,如果在你的代码中,会需要处理一只常量或者变量没有值的情况,那么请把它声明为合适的可选类型。
如果你定义了一个可选类型的变量,但是没有给予默认值,那么将会自动给这个变量赋值为nil。
var surveyAnswer: String? // surveyAnswer 被自动设置为nil
NOTE:Swift中的nil和OC中的nil是不一样的。OC中的nil是一个指针,指向了一个其实不存在的对象。Swift中的nil不是指针,它表示某一个可选类型没有值。任何可选类型都可以被赋值为nil,而不仅限于对象类型。
If语句和强制解包(If Statements and Forced Unwrapping)
可以在If语句通过和nil比较来判断一个可选值是不是包含值,你可以使用比较操作符 == 或者 !=。
如果一个可选值有值,那么它被认为是不等于nil的:
let convertedNumber:Int? = 123
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 打印 "convertedNumber contains some integer value.
一旦你确定一个可选值是包含了值的,那就可以在变量名后添加感叹号访问可选值内在的值。这个感叹号就意味着:我知道这个可选值肯定包含了一个值,老子要用它了。这就是所谓的对于可选值的强制解包:
“if convertedNumber != nil {
print("convertedNumber has an integer value of \ (convertedNumber!).")
}
// 打印 "convertedNumber has an integer value of 123.
可选值的绑定(Optional Binding)
也可以使用绑定可选值去判断是否包含了一个值,如果要这样做,那么把那个值转换成一个可以访问的临时常量或者变量。在If 语句和switch语句中,都可以使用单独的绑定可选值的操作去检查一个可选值里面包含的值,并且取出包含的值赋值给常量或者变量。
如何在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")
}
// 打印 ""123" has an integer value of 123
这段代码可以读为:
如果Int(possibleNumber)返回的Int?的值包含一个值,用可选值包含的值赋值给actualNumber常量。
如果转换成功了,常量actualNumber在if语句的第一个分支中是可用的,它已经被可选值包含的值的初始化了,不需要再用后缀感叹号去访问他的值。例子中,只是简单打印了一下转换后的结果。
常量和变量都进行可选值的绑定,如果在if语句的第一个分支里面需要操作actualNumber,应该写为if var actualNumber。那么可选值中包含的值将会用来初始化一个可以被改变的变量而不是常量了。
如果需要,可以在一个if语句中包含多个可选值绑定和布尔值的条件判断,使用逗号分隔。如果某一个可选值绑定的结果是nil,或者某一个布尔条件是false,那么整个if语句的条件判断都会被认为是false,下面两个if语句是一样的:
“if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber <secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印 "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// 打印 "4 < 42 < 100”
NOTE:使用条件绑定创建的常量或者变量只在if语句的代码块中可用。相反的,使用guard语句创建的常量或者变量只在guard语句后面的代码中可用。
隐式解包可选值(Implicitly Unwrapped Optionals)
正如上面描述的一样,可选值表明一个常量或者变量可以允许没有值。可选值可以使用If语句来检查是否存在值,并且可以使用可选值绑定的方式去访问可选值包含的值,前提是可选值确实包含了可用的值。
有些时候,可以从程序的结构中发现,一个可选值在第一次赋值之后总是有值的。这些情况下,每次需要访问的值的时候不需要再去检查而直接解包就可以了,因为可以毫无顾虑的认为它无论何时都有值存在。
这种可选值会被定义为允许隐式解包的可选值。只要在定义可选值的时候在类型后面跟上感叹号(!)而不要跟上(?)就好了。
如果一个可选值在定义之后可以确定在往后的所有时间点都存在值,那么隐式解包是很有用的。隐式解包的基本运用主要是在初始化类的时候。
一个允许隐式解包的可选值在程序中其实还是一个正常的可选值,但是可以像使用一个非可选值一样使用,而不需要在每一次需要访问值的时候进行解包操作。下面的例子可以说明普通的可选值和允许隐式解包的可选值在访问他们包含的值的时候的区别,一个允许隐式解包的值访问就像访问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
你可以认为隐式解包的可选值是给了一个可选值在被访问的时候自动解包的权限。使用隐式解包,只需要声明的时候在可选类型后面添加感叹号,而不需要每次使用的时候需要在名字后面加上感叹号进行解包。
NOTE: 如果一个允许隐式解包的可选值是nil,但是你去访问了它包含的值,那么你将会触发一个运行时错误。这和对一个值是nil的可选值进行强制解包的结果是一样的。
对于允许隐式解包的可选值,也可以使用If语句去检查它是不是存在值:
if assumedString != nil {
print(assumedString)
}
// 打印 "An implicitly unwrapped optional string.
也可以使用可选值绑定过去检查和解包它包含的值:
if let definiteString = assumedString {
print(definiteString)
}
// 打印 "An implicitly unwrapped optional string.
NOTE:一个可选值在后面的某哥时候有nil的可能,那么请不要使用隐式解包。
如果在使用一个可选的变量的时候你总是需要检查一个变量是不是nil,使用普通的可选类型是比较好的选择。