Swift 是一门类型安全的语言,这意味着所有的类型必须在编译时就已经确定。然而,在真实的情况下,我们通常会遇到需要使用变量、属性或方法的场景,但是这些对象并不总是有效存在的,例如一个可能为空的变量。针对这种情况,Swift 提供了可选类型。可选类型允许我们表示一个值不存在的情况。
当我们尝试访问一个空的可选类型变量的成员时,程序就会崩溃,并抛出一个运行时错误。为了避免这种情况,Swift 引入了可选链。
可选链提供了一种安全地调用方法、属性、下标等成员的方式,即使这个成员可能为空,也能够避免程序崩溃。
可选链的语法
通过在调用成员之前加上问号(?
),我们可以创建一个可选链。如果该成员存在,则返回它的值;否则,整个可选链表达式返回 nil。
以下是可选链的基本语法:
1
2
3
optionalValue?.method()
optionalValue?.property
optionalValue?[index]
其中 optionalValue
是一个可能为空的值。如果 optionalValue
为空,那么整个表达式将直接返回 nil,而不是引发运行时错误。
注意,可选链只是一种安全调用成员的方法,它并不修改被调用的成员,因此即使整个可选链表达式返回 nil,被调用的成员仍然保持不变。
调用方法
假设我们有一个 Person 类型的实例,它包含一个名为 fullName()
的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func fullName() -> String {
return "\(self.firstName) \(self.lastName)"
}
}
let person: Person? = Person(firstName: "John", lastName: "Doe")
现在,我们可以通过可选链来调用 person
实例的 fullName()
方法:
1
let fullName = person?.fullName()
如果 person
实例为空,那么 fullName
的值将是 nil。否则,它将包含 fullName()
方法的返回值。
访问属性
除了调用方法外,我们还可以使用可选链来访问属性。假设我们有一个类似于下面的 Car 类型:
1
2
3
4
5
6
7
8
class Car {
var model: String
var price: Int?
init(model: String) {
self.model = model
}
}
现在,我们创建了一个 Car
实例,并尝试使用可选链访问它的 price
属性:
1
2
let car: Car? = Car(model: "BMW")
let price = car?.price
如果 car
实例为空,那么 price
的值将是 nil。否则,它将包含 price
属性的值,该属性本身也是一个可选类型。
访问下标
与访问属性和调用方法类似,我们也可以使用可选链来访问下标。假设我们有一个名为 matrix
的二维数组,我们可以使用可选链来访问数组元素:
1
2
var matrix: [[Int]]?
let x = matrix?[0][0]
如果 matrix
为空,那么 x
的值将是 nil。如果 matrix
不为空,但是第一个子数组为空,那么依然会返回 nil。
链接多层可选链
当我们需要链接多个可选链时,我们可以使用多个问号(??
)来链接它们。例如:
1
let price = car?.manufacturer?.address?.location?.latitude ?? 0.0
在这个例子中,我们链接了多个可选链来访问 car
实例的制造商的地址的位置纬度。如果任何一个链接为空,那么整个表达式将返回默认值 0.0。
忽略可选链
如果我们确定一个可选链一定不为空,可以使用感叹号(!
)来忽略它的可选性。这被称为强制解包。
例如,假设我们有一个非空的字符串数组:
1
let names = ["Alice", "Bob", "Charlie”]
然后我们可以使用可选链来获取第一个元素的首字母:
1
let firstInitial = names.first?.prefix(1)
在这种情况下,first
的返回类型是 String?
,因此 prefix(_:)
方法也返回了一个可选类型的 Substring?
。但是,由于我们已经知道 names
数组中至少有一个元素,所以我们可以使用强制解包来忽略这些可选性:
1
let firstInitial = names.first!.prefix(1)!
这里需要注意的是,如果我们错误地对空数组进行强制解包,程序会崩溃并抛出一个运行时错误。
可选链和隐式拆包可选类型
Swift 中还有一种叫做隐式拆包可选类型的类型。这些类型类似于可选类型,但是在访问它们的成员时可以省略问号,因为编译器自动将其解包。
在使用可选链时,隐式拆包可选类型表现得就像普通的可选类型一样。如果我们在可选链中访问隐式拆包可选类型的属性或方法,它仍然可能为空,并且仍然需要使用问号来避免程序崩溃。
以下是一个使用隐式拆包可选类型的示例:
1
2
let label: UILabel! = UILabel()
let text = label?.text
在这个例子中,label
是一个隐式拆包可选类型,它的 text
属性也是一个可选类型。如果 label
为空,则 text
的值将是 nil。