通过在想调用的属性、方法,或下标的可选值后面放一个问号(?
),可以定义一个可选链。这一点很像在可选值后面放一个叹号(!
)来强制解包它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制解包将会触发运行时错误。
为了反映可选链式调用可以在空值(nil
)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回 nil
则说明调用失败。
这里需要特别指出,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是 Int
类型,则会变为 Int?
类型。
下面几段代码将解释可选链式调用和强制解包的不同。
首先定义两个类 Person
和 Residence
:
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
Residence
有一个 Int
类型的属性 numberOfRooms
,其默认值为 1
。Person
具有一个可选的 residence
属性,其类型为 Residence?
。
假如你创建了一个新的 Person
实例,它的 residence
属性由于是可选类型而将被初始化为 nil
,在下面的代码中,john
有一个值为 nil
的 residence
属性:
let john = Person()
如果使用叹号(!
)强制解包获得这个 john
的 residence
属性中的 numberOfRooms
值,会触发运行时错误,因为这时 residence
没有可以解包的值:
let roomCount = john.residence!.numberOfRooms
// 这会引发运行时错误
john.residence
为非 nil
值的时候,上面的调用会成功,并且把 roomCount
设置为 Int
类型的房间数量。正如上面提到的,当 residence
为 nil
的时候,上面这段代码会触发运行时错误。
可选链式调用提供了另一种访问 numberOfRooms
的方式,使用问号(?
)来替代原来的叹号(!
):
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打印“Unable to retrieve the number of rooms.”
在 residence
后面添加问号之后,Swift 就会在 residence
不为 nil
的情况下访问 numberOfRooms
。
因为访问 numberOfRooms
有可能失败,可选链式调用会返回 Int?
类型,或称为“可选的 Int
”。如上例所示,当 residence
为 nil
的时候,可选的 Int
将会为 nil
,表明无法访问 numberOfRooms
。访问成功时,可选的 Int
值会通过可选绑定解包,并赋值给非可选类型的 roomCount
常量。
要注意的是,即使 numberOfRooms
是非可选的 Int
时,这一点也成立。只要使用可选链式调用就意味着 numberOfRooms
会返回一个 Int?
而不是 Int
。
可以将一个 Residence
的实例赋给 john.residence
,这样它就不再是 nil
了:
john.residence = Residence()
john.residence
现在包含一个实际的 Residence
实例,而不再是 nil
。如果你试图使用先前的可选链式调用访问 numberOfRooms
,它现在将返回值为 1
的 Int?
类型的值:
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打印“John's residence has 1 room(s).”
下一节:Swift 是一种非常好的编写软件的方式,无论是手机,台式机,服务器,还是其他运行代码的设备。它是一种安全,快速和互动的编程语言,将现代编程语言的精华和苹果工程师文化的智慧,以及来自开源社区的多样化贡献结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。