在值可能不存在的情况下使用可选值(optional), 可选值是:
· 存在一个值,这个值等于 x
或
· 不存在任何值
注:
在 C 和 Objective-C 中可选值的概念并不存在。Objective-C 中(与可选值)最相近的(概念)是,一个以对象为返回值的方法,也可以返回 nil,nil表示“不存在有效的对象”。不过,这规则只对对象有效——对于结构、基本的 C 类型或枚举值无效。对于这些类型,Objective-C 语言的方法通常会返回一个特殊值(如
NSNotFound
)来表示值不存在。这种策略假定该方法的调用方知道要测试返回值是否等于某个特殊值,并且记得要作此检查。Swift 的可选值允许表示任何类型不存在值,无需定义特殊常量。
这里有个例子。Swift 的 String
类型有一个名为 toInt
的方法,可尝试将 String
值转为 Int
值。然而,不是所有字符串都可以转换为整数。字符串 “123”
可以转换为数值 123
,而字符串 “hello, world”
显然没有对应的数值。
下面的例子会尝试用 toInt
方法将 String
转换为 Int
:
1 let possibleNumber(可能是数字) = "123" 2 let convertedNumber(转换得到的数字) = possibleNumber.toInt()
3 //convertedNumber被推断为 "Int?" 类型,或 "可选的 Int"
因为 toInt
方法可能会转换失败,因此它返回的是一个 可选的 Int
型,而不是 Int
型。可选的 Int
写法为 Int?
,而不是 Int
。问号表明示该类型包含的值是可选的,即 Int?
可能包含某个 Int
类型的值,也可能不含任何值。(它不能包含其他类型的值,如 Bool
值或 String
值,它要么是 Int 要么就
不存在。)
if
语句与强制拆包
可使用 if
语句测试可选值是否含有值。如果有,则求值结果返回 true
,否则返回 false
。
一旦确认可选值的确含有一个值,就可以通过在可选值变量名的末尾添加感叹号(!
)来访问其内部的值。感叹号明确表达:“我知道这个可选值确实存在值,请使用它(那个值)吧!”这种操作名为对可选值进行强制拆包(force-unwrap):
-
1 if convertedNumber(转换后得到的数字) { 2 println("\(possibleNumber) 的整数值为 \(convertedNumber!)") 3 } else { 4 println("\(possibleNumber) 不能转换为一个整数") 5 } 6 // 输出 "123 的整数值为 123"
关于 if
语句的更多信息,请见 流程控制。
注:
尝试用
!
访问不存在的可选值时会在运行时报错,在用!
强行拆包之前,务必确保可选值确实包含一个非nil
的值。
可选值绑定
可以使用可选值绑定(optional binding)测试可选值是否包含一个值,如果有,则将该值以临时常量或变量的形式拆包备用。可选值绑定可以与 if
或 while
语句结合使用,这样只需要一步就可以检查是否存在值、提取该值、并存放到常量或变量中。关于 if
与 while
语句的更多信息在 流程控制 一章中有讲过。
可选值绑定与 if 混用可以是这样:
1 if let constantName = someOptional { 2 statements 3 }
上文中的 possibleNumber 一例,可以用可选值绑定代替强制拆包:
1 if let actualNumber = possibleNumber.toInt() { 2 println("\(possibleNumber) 的整数值为 \(actualNumber)") 3 } else { 4 println("\(possibleNumber) 无法转换为整数") 5 } 6 // 输出 "123 的整数值为 123"
可以这样理解:
“如果 possibleNumber.toInt()
返回的 可选的 Int
包含一个值,则新建一个名为 actualNumber
的常量,并将常量的值设为可选值中包含的值。”
如果转换成功,常量 actualNumber
将可供 if
语句的第一个分支语句使用。该常量已经被可选值包含的值初始化,因此不再需要用后缀 !
访问其值。本例中,actualNumber
被直接用来输出转换结果。
常量与变量均可用于可选值绑定。如果需要在第一个分支中处理 actualNumber 的值,可以改写为 if var actualNumber
,这样可选值的值将作为变量而非常量拆包。
nil
要将可选变量设为空值(没有值或值不存在的状态,valueless),可以给它赋特殊值 nil
:
1 var serverResponseCode(服务器状态码): Int? = 404 2 //服务器状态码包含一个实际存在的 Int 值:404
3 serverResponseCode= nil
4 // 服务器状态码 现在不含任何值
注:
nil
不能用于非可选值的常量或变量。如果代码中的常量或变量需要适配值不存在的某种特殊情况,务必将它声明为适用的可选值类型。
如果你所定义的可选值未提供默认值,该常量或变量将被自动设为 nil
:
1 var surveyAnswer: String? 2 // surveyAnswer 被自动设为 nil
注:
Swift 的
nil
与 Objective-C 的nil
不同。Objective-C 的nil
是指向一个不存在对象的指针,而 Swift 的nil
不是指针——它是一种表示值不存在的特殊类型。任何类型的可选值都能赋值为nil
,不仅限于对象类型。
可选值的隐式拆包
如上所述,可选值指允许“值不存在”的常量或变量。可以通过 if
语句测试可选值是否存在值,也可以通过可选值绑定按条件拆包,并在该可选值的值存在的情况下才访问它的值。
有时根据程序结构可以知晓一个可选值在首次赋值后,必然存在值。这时,可以不必每次访问时都检测并提取该可选值的值,因为可以安全地假设它那时一定存在值。
这些可选值可定义为隐式拆包的可选值(implicitly unwrapped optional)。你可以通过在希望标为可选的类型名称后面,用感叹号 (String!
) 代替问号 (String?
)的方式书写隐式拆包可选值。
隐式拆包的可选值在可选值首次定义后即确认存在值,在此之后任何时刻都能被明确地(肯定有值)使用。Swift 中隐式拆包可选值主要应用于类初始化,详见 外部引用与隐式拆包的可选属性(待译)。
隐式拆包的可选值在运行环境里就是普通的可选值,但能够像非可选值那样使用,无需在每次访问时拆包。下例显示了 可选的 String
与 隐式拆包的可选 String
之间的行为差异:
1 let possibleString(可能的字符串): String? = "An optional String。(一个可选的String)" 2 println(possibleString!) // 访问其值时需要添加感叹号 3 // 输出 "可选的 String。" 4 let assumedString(假设的字符串): String! = "An implicitly unwraped optional String。(隐式的拆包的可选Stirng)"
5 println(assumedString) // 访问其值时无需感叹号
6 // 输出 "隐式拆包的可选 String。"
可以认为,隐式拆包的可选值即授予可选值在被使用时自动拆包的权限(即无需再次手动进行拆包)。不必每次使用可选值时都在名称后面添加感叹号,只需在声明时在可选值类型后面加上感叹号即可。
注:如果在隐式拆包的可选值存在值之前就尝试访问(不存在值时不可进行隐式拆包),运行时会报错。结果与在普通可选值尚未赋值时直接加感叹号就引用相同。
隐式拆包的可选值也可以当作像普通可选值那样,检查是否存在值:
1 if assumedString { 2 println(assumedString) 3 } 4 // 输出 "An implicitly unwraped optional String。(隐式的拆包的可选Stirng)"
隐式拆包的可选值同样可以使用可选值绑定,单条语句完成检查值并拆包:
1 if let definiteString(肯定是字符串) = assumedString(假定是字符串) { 2 println(definiteString) 3 } 4 // 输出 "隐式拆包的可选 String。"
注:
当变量在后续过程中可能变为
nil
时,不应使用隐式拆包的可选值。如果在变量声明周期内都需要检查nil
值,请务必使用普通的可选类型量。
谢谢,Swifter-QQ群:362232993,同好者进~
Fork:https://github.com/Joejo/Swift-lesson-for-chinese