可選鏈(Optional Chaining)是一種可以請求和調(diào)用屬性夺姑、方法和子腳本的過程,用于請求或調(diào)用的目標(biāo)可能為nil。
可選鏈返回兩個值:
如果目標(biāo)有值迈倍,調(diào)用就會成功择浊,返回該值
如果目標(biāo)為nil戴卜,調(diào)用將返回nil
多次請求或調(diào)用可以被鏈接成一個鏈,如果任意一個節(jié)點(diǎn)為nil將導(dǎo)致整條鏈?zhǔn)А?/p>
可選鏈可替代強(qiáng)制解析
通過在屬性琢岩、方法投剥、或下標(biāo)腳本的可選值后面放一個問號(?),即可定義一個可選鏈担孔。
可選鏈 '?' 感嘆號(!)強(qiáng)制展開方法江锨,屬性,下標(biāo)腳本可選鏈
? 放置于可選值后來調(diào)用方法糕篇,屬性啄育,下標(biāo)腳本 ! 放置于可選值后來調(diào)用方法,屬性拌消,下標(biāo)腳本來強(qiáng)制展開值
當(dāng)可選為 nil 輸出比較友好的錯誤信息 當(dāng)可選為 nil 時強(qiáng)制展開執(zhí)行錯誤
使用感嘆號(!)可選鏈實(shí)例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
//將導(dǎo)致運(yùn)行時錯誤
let roomCount = john.residence!.numberOfRooms
以上程序執(zhí)行輸出結(jié)果為:
fatal error: unexpectedly found nil while unwrapping an Optional value
想使用感嘆號(!)強(qiáng)制解析獲得這個人residence屬性numberOfRooms屬性值挑豌,將會引發(fā)運(yùn)行時錯誤,因?yàn)檫@時沒有可以供解析的residence值拼坎。
使用問號(?)可選鏈實(shí)例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
// 鏈接可選residence?屬性浮毯,如果residence存在則取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
print("John 的房間號為 \(roomCount)。")
} else {
print("不能查看房間號")
}
以上程序執(zhí)行輸出結(jié)果為:
不能查看房間號
因?yàn)檫@種嘗試獲得numberOfRooms的操作有可能失敗泰鸡,可選鏈會返回Int?類型值债蓝,或者稱作"可選Int"。當(dāng)residence是空的時候(上例)盛龄,選擇Int將會為空饰迹,因此會出現(xiàn)無法訪問numberOfRooms的情況芳誓。
要注意的是,即使numberOfRooms是非可選Int(Int?)時這一點(diǎn)也成立啊鸭。只要是通過可選鏈的請求就意味著最后numberOfRooms總是返回一個Int?而不是Int锹淌。
為可選鏈定義模型類
你可以使用可選鏈來多層調(diào)用屬性,方法赠制,和下標(biāo)腳本赂摆。這讓你可以利用它們之間的復(fù)雜模型來獲取更底層的屬性,并檢查是否可以成功獲取此類底層屬性钟些。
實(shí)例
定義了四個模型類烟号,其中包括多層可選鏈:
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
通過可選鏈調(diào)用方法
你可以使用可選鏈的來調(diào)用可選值的方法并檢查方法調(diào)用是否成功政恍。即使這個方法沒有返回值汪拥,你依然可以使用可選鏈來達(dá)成這一目的。
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms篙耗,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if ((john.residence?.printNumberOfRooms()) != nil) {
print("輸出房間號")
} else {
print("無法輸出房間號")
}
以上程序執(zhí)行輸出結(jié)果為:
無法輸出房間號
使用if語句來檢查是否能成功調(diào)用printNumberOfRooms方法:如果方法通過可選鏈調(diào)用成功迫筑,printNumberOfRooms的隱式返回值將會是Void,如果沒有成功宗弯,將返回nil脯燃。
使用可選鏈調(diào)用下標(biāo)腳本
你可以使用可選鏈來嘗試從下標(biāo)腳本獲取值并檢查下標(biāo)腳本的調(diào)用是否成功,然而罕伯,你不能通過可選鏈來設(shè)置下標(biāo)腳本曲伊。
實(shí)例1
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if let firstRoomName = john.residence?[0].name {
print("第一個房間名 \(firstRoomName).")
} else {
print("無法檢索到房間")
}
以上程序執(zhí)行輸出結(jié)果為:
無法檢索到房間
在下標(biāo)腳本調(diào)用中可選鏈的問號直接跟在 circname.print 的后面追他,在下標(biāo)腳本括號的前面坟募,因?yàn)閏ircname.print是可選鏈試圖獲得的可選值。
實(shí)例2
實(shí)例中創(chuàng)建一個Residence實(shí)例給john.residence邑狸,且在他的rooms數(shù)組中有一個或多個Room實(shí)例懈糯,那么你可以使用可選鏈通過Residence下標(biāo)腳本來獲取在rooms數(shù)組中的實(shí)例了:
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客廳"))
johnsHouse.rooms.append(Room(name: "廚房"))
john.residence = johnsHouse
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("John 所在的街道是 \(johnsStreet)单雾。")
} else {
print("無法檢索到地址赚哗。 ")
}
以上程序執(zhí)行輸出結(jié)果為:
John 所在的街道是 Laurel Street。
通過可選鏈接調(diào)用來訪問下標(biāo)
通過可選鏈接調(diào)用硅堆,我們可以用下標(biāo)來對可選值進(jìn)行讀取或?qū)懭胗齑ⅲ⑶遗袛嘞聵?biāo)調(diào)用是否成功。
實(shí)例
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms渐逃,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客廳"))
johnsHouse.rooms.append(Room(name: "廚房"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("第一個房間名為\(firstRoomName)")
} else {
print("無法檢索到房間")
}
以上程序執(zhí)行輸出結(jié)果為:
第一個房間名為客廳
訪問可選類型的下標(biāo)
如果下標(biāo)返回可空類型值够掠,比如Swift中Dictionary的key下標(biāo)∏丫眨可以在下標(biāo)的閉合括號后面放一個問號來鏈接下標(biāo)的可空返回值:
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
上面的例子中定義了一個testScores數(shù)組疯潭,包含了兩個鍵值對赊堪, 把String類型的key映射到一個整形數(shù)組。
這個例子用可選鏈接調(diào)用把"Dave"數(shù)組中第一個元素設(shè)為91竖哩,把"Bev"數(shù)組的第一個元素+1哭廉,然后嘗試把"Brian"數(shù)組中的第一個元素設(shè)為72。
前兩個調(diào)用是成功的相叁,因?yàn)檫@兩個key存在遵绰。但是key"Brian"在字典中不存在,所以第三個調(diào)用失敗钝荡。
連接多層鏈接
你可以將多層可選鏈連接在一起街立,可以掘取模型內(nèi)更下層的屬性方法和下標(biāo)腳本舶衬。然而多層可選鏈不能再添加比已經(jīng)返回的可選值更多的層埠通。
如果你試圖通過可選鏈獲得Int值,不論使用了多少層鏈接返回的總是Int?逛犹。 相似的端辱,如果你試圖通過可選鏈獲得Int?值,不論使用了多少層鏈接返回的總是Int?虽画。
實(shí)例1
下面的例子試圖獲取john的residence屬性里的address的street屬性舞蔽。這里使用了兩層可選鏈來聯(lián)系residence和address屬性,它們兩者都是可選類型:
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms码撰,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if let johnsStreet = john.residence?.address?.street {
print("John 的地址為 \(johnsStreet).")
} else {
print("不能檢索地址")
}
以上程序執(zhí)行輸出結(jié)果為:
不能檢索地址
實(shí)例2
如果你為Address設(shè)定一個實(shí)例來作為john.residence.address的值渗柿,并為address的street屬性設(shè)定一個實(shí)際值,你可以通過多層可選鏈來得到這個屬性值脖岛。
class Person {
var residence: Residence?
}
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get{
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
class Room {
let name: String
init(name: String) { self.name = name }
}
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
john.residence?[0] = Room(name: "浴室")
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客廳"))
johnsHouse.rooms.append(Room(name: "廚房"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("第一個房間是\(firstRoomName)")
} else {
print("無法檢索房間")
}
以上實(shí)例輸出結(jié)果為:
第一個房間是客廳
對返回可選值的函數(shù)進(jìn)行鏈接
我們還可以通過可選鏈接來調(diào)用返回可空值的方法朵栖,并且可以繼續(xù)對可選值進(jìn)行鏈接。
實(shí)例
class Person {
var residence: Residence?
}
// 定義了一個變量 rooms柴梆,它被初始化為一個Room[]類型的空數(shù)組
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房間號為 \(numberOfRooms)")
}
var address: Address?
}
// Room 定義一個name屬性和一個設(shè)定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最終類叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if john.residence?.printNumberOfRooms() != nil {
print("指定了房間號)")
}? else {
print("未指定房間號")
}
以上程序執(zhí)行輸出結(jié)果為:
未指定房間號