文章目录
- 什么是可选类型?
- nil
- if 语句以及强制解析
- 可选绑定
- 隐式解析可选类型
什么是可选类型?
Swift 的可选(Optional)类型,用于处理值缺失的情况。可选表示"那儿有一个值,并且它等于 x “或者"那儿没有值”。
Swfit语言定义后缀?作为命名类型Optional的简写,换句话说,以下两种声明是相等的:
var optionalInteger: Int?
var optionalInteger: Optional<Int>
在这两种情况下,变量 optionalInteger 都是可选整数类型。注意,在类型和 ?之间没有空格。
Optional 是一个含有两种情况的枚举,None 和 Some(T),用来表示可能有或可能没有值。任何类型都可以明确声明为(或者隐式转换)可选类型。当声明一个可选类型的时候,要确保用括号给 ? 操作符一个合适的范围。例如,声明可选整数数组,应该写成 (Int[])? 写成 Int[]? 会报错。
当你声明一个可选变量或者可选属性的时候没有提供初始值,它的值会默认为 nil。
可选项遵照 LogicValue 协议,因此可以出现在布尔环境中。在这种情况下,如果可选类型T?包含类型为T的任何值(也就是说它的值是 Optional.Some(T) ),这个可选类型等于 true,反之为 false。
※注意:
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回 nil,nil 表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如 NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值。
如果一个可选类型的实例包含一个值,你可以用后缀操作符 !
来访问这个值,如下所示:
optionalInteger = 42
optionalInteger! // 42
下面的例子使用这种构造器来尝试将一个 String 转换成 Int:
let possibleNumber = "123";
let convertedNumber = Int(possibleNumber);
print(type(of: convertedNumber));
以上代码输出结果为:
Optional<Int>
因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)
nil
你可以给可选变量赋值为 nil
来表示它没有值:
// serverResponseCode 包含一个可选的 Int 值 404
var serverResponseCode: Int? = 404;
// serverResponseCode 现在不包含值
serverResponseCode = nil;
※注意:
nil
不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil
:
// surveyAnswer 被自动设置为 nil
var surveyAnswer: String?
※注意:
Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。
if 语句以及强制解析
你可以使用 if
语句和 nil
比较来判断一个可选值是否包含值。你可以使用“相等”(==
)或“不等”(!=
)来执行比较。如果可选类型有值,它将不等于 nil
。
let possibleNumber = "123";
let convertedNumber = Int(possibleNumber);
if convertedNumber != nil {
print("convertedNumber contains some integer value.");
}
// 输出“convertedNumber contains some integer value.”
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!
)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
//convertedNumber has an integer value of 123.
※注意:
使用!
来获取一个不存在的可选值会导致运行时错误。使用!
来强制解析值之前,一定要确定可选包含一个非nil
的值。
完整示例:
var myString: String?
myString = "Hello, Swift!"
if myString != nil {
print(myString);
print(myString!);
}else{
print("myString 值为 nil")
}
上述代码输出结果为:
Optional("Hello, Swift!")
Hello, Swift!
可选绑定
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
像下面这样在 if 语句中写一个可选绑定:
if let constantName = someOptional {
statements
}
你可以像上面这样使用可选绑定来重写 在 可选类型 举出的 possibleNumber 例子:
let possibleNumber = "123";
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 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。在这个例子中,actualNumber 只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在 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")
}
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
上述代码输出的结果为:
4 < 42 < 100
4 < 42 < 100
※注意:
在 if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值,请参考 提前退出
隐式解析可选类型
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 if 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 无主引用以及隐式解析可选属性。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别:
let possibleString: String? = "An optional string.";
let forcedString: String = possibleString!; // 需要感叹号来获取值
print("possibleString: \(type(of: possibleString))");
print("forcedString: \(type(of: forcedString))");
let assumedString: String! = "An implicitly unwrapped optional string.";
let implicitString: String = assumedString; // 不需要感叹号
print("assumedString: \(type(of: assumedString))");
print("implicitString: \(type(of: implicitString))");
上述代码输出结果:
possibleString: Optional<String>
forcedString: String
assumedString: Optional<String>
implicitString: String
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把 !
放到类型的结尾,而不是每次取值的可选名字的结尾。
※注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
if assumedString != nil {
print(assumedString!)
}
上述代码输出结果:
An implicitly unwrapped optional string.
你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:
if let definiteString = assumedString {
print(definiteString);
}
上述代码输出结果
An implicitly unwrapped optional string.
let newString: String? = nil
if let printStr = newString {
print(printStr);
}else {
print(newString);
}
上述代码输出结果
nil
※注意:
如果一个变量之后可能变成nil
的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil
的话,请使用普通可选类型。