Swift可选拆包

前言

You use optionals in situations where a value may be absent. An optional represents two possibilities: Either there is a value, and you can unwrap the optional to access that value, or there isn’t a value at all.

可选用来表示值的或缺,当可选没有值时相当于OC中的nil,而当有值时我们需要对可选进行拆包才能取出和访问这个值。本篇将对可选的拆包方式做一次汇总~

1.强制拆包

Once you’re sure that the optional does contain a value, you can access its underlying value by adding an exclamation mark (!) to the end of the optional’s name. The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.” This is known as forced unwrapping of the optional’s value:

强制拆包是指当确定可选有值时,在可选后标注一个!,表明“我确定可选有值,请给我值”。

#示例:

1
2
3
4
5
6
7
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"

print("convertedNumber has an integer value of \(convertedNumber!).") // 这个convertedNumber!即为强制拆包

// Prints "convertedNumber has an integer value of 123."

2.可选绑定

You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.

可选绑定是当可选有值时将值绑定到一个临时变量或常量中,多用在ifwhile语句中。

#示例1:

1
2
3
4
5
6
if let actualNumber = Int(possibleNumber) { // 有值时绑定到临时常量actualNumber中
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"

精简版用法:

1
2
3
4
5
let nickname: String? = nil
if let nickname { // 省略了赋值:if let x = nickName
print("Hey, \(nickname)") // 使用同名变量代替拆包后的值
}
// Doesn't print anything, because nickname is nil.

#示例2:if中多个可选绑定组合

1
2
3
4
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

3.隐式拆包变量声明

Sometimes it’s clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it’s useful to remove the need to check and unwrap the optional’s value every time it’s accessed, because it can be safely assumed to have a value all of the time.
These kinds of optionals are defined as implicitly unwrapped optionals. You write an implicitly unwrapped optional by placing an exclamation mark (String!) rather than a question mark (String?) after the type that you want to make optional.

有时当可选被首次赋值之后,能确定此可选后续必定有值,且不会再被设置为nil。这时为了不必每次访问可选变量时都进行拆包或检查,可在声明可选时往其后面标注一个!,这种可选就是隐式拆包的可选。

使用隐式拆包时,可以像普通可选一样进行可选绑定,也可以像非可选变量一样不必在其后加!

#示例:

1
2
3
4
5
6
7
8
9
10
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 let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."

4.可选链

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

当可选对象调用属性、方法或下标时,可选本身可能为nil。如果为nil,则调用失败;如果不为nil,则调用成功。

多个调用可以链接到一起并返回一个可选值,一旦其中某个调用返回nil时,整个调用链就会失败。

#示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
var residence: Residence?
}

class Residence {
var numberOfRooms: Int {
get {
print("++numberOfRooms is 1)")
return 1
}
set {
}
}
}

let john = Person()
let opt = john.residence?.numberOfRooms // opt is a Int? type

if let roomCount = opt {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

这里的”john.residence?.numberOfRooms”就是一个可选链,此可选链返回一个Int?类型的可选opt,将其拆包即可。

变量john初始化时其residence对象并未赋值,即为nil,所以整个可选链调用失败,也不会再访问后续的numberOfRooms属性。因为可选链失败了,所以对opt进行可选绑定时取不到值,因此输出最后一行~

5.空合运算符??

The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.

空合运算符在可选a有值时会对a进行拆包并返回拆包后的值a!;如果可选a没有值则返回默认值b

b的值必须与a的类型一致。

空合运算符是下面代码的缩写:

1
a != nil ? a! : b

6.guard模式匹配

A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else clause is executed if the condition is not true.

guardif语句一样,都是根据紧随其后的 Boolean 型表达式而决定后续行为,不同的是guard的声明中一定要有一个else代码块。在guard语句中,如果表达式为真才会继续执行guard声明之后的代码,否则就执行其else代码块中的代码。

基于这些,我们可以像在if语句中一样,在guard中使用可选绑定进行拆包。

#示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func greet(person: [String: String]) {
guard let name = person["name"] else { // 可选绑定
return
}

print("Hello \(name)!")

guard let location = person["location"] else { // 可选绑定
print("I hope the weather is nice near you.")
return
}

print("I hope the weather is nice in \(location).")
}

greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."

结尾

以上几种就是常见的拆包方法,1和3是强制拆包,有可能出错所以不安全,建议使用其他几种方法进行拆包~


相关参考:

#©Swift.org


Swift可选拆包
https://davidlii.cn/2018/08/18/swift-unwrap.html
作者
Davidli
发布于
2018年8月18日
许可协议