在值可能不存在的情况下使用可选值(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 class="vc">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 转换得到的数字 { 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 }
上文中 可能是数字
一例,可以改写用可选值绑定代替强制拆包:
1 if let actualNumber = possibleNumber.toInt() { 2 println("\(possibleNumber) 的整数值为 \(actualNumber)") 3 } else { 4 println("\(possibleNumber) 无法转换为整数") 5 } 6 // 输出 "123 的整数值为 123"
可以这样理解:
“如果 可能是数字.toInt
返回的 可选的 Int
包含一个值,则新建一个名为 实际值
的常量,并将其值设为可选值中包含的值。”
如果转换成功,常量 实际值
将可供 if
语句的第一段分支使用。该常量已经以可选值内部的值初始化,因此不再需要用后缀 !
访问其值。本例中,实际值
被直接用来输出转换结果。
常量与变量均可用于可选值绑定。如果需要在第一个分支中修改 实际值
的值,可以改写为 if var 实际值
,这样可选值的值将作为变量而非常量拆包。
要将可选变量设为值不存在的状态,可以给它赋特殊值 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