可空鏈?zhǔn)秸{(diào)用是一種可以請(qǐng)求和調(diào)用屬性、方法以及下標(biāo)的過(guò)程,它的可空體現(xiàn)在請(qǐng)求或調(diào)用的目標(biāo)當(dāng)前可能為nil。如果可空的目標(biāo)有值,即調(diào)用就會(huì)成功;如果選擇的目標(biāo)為nil,即調(diào)用將返回nil。多個(gè)連續(xù)的調(diào)用可以被鏈接在一起形成一個(gè)調(diào)用鏈,如果其中任何一個(gè)節(jié)點(diǎn)為nil將導(dǎo)致整個(gè)鏈調(diào)用失敗。
一、使用可空鏈?zhǔn)秸{(diào)用來(lái)強(qiáng)制展開(kāi)
可空鏈的定義,即是在要調(diào)用非空的屬性、方法、下標(biāo)的可空值后面添加一個(gè)問(wèn)號(hào)即可。特別的,可空鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可空類型值,當(dāng)可空鏈?zhǔn)秸{(diào)用成功時(shí),一個(gè)本該返回Int類型的結(jié)果將返回Int?類型。
class Person {
// 可空屬性,類型為Student?
var student:Student?
}
class Student {
var name = "xxx"
}
let zhangsan = Person()
// 這是錯(cuò)誤的,因?yàn)閦hangsan.student現(xiàn)在是為`nil`
//let name = zhangsan.student!.name
// 可空鏈?zhǔn)秸{(diào)用,即是使用`?`來(lái)代替`!`
// 即是說(shuō),只有當(dāng)student不為空的時(shí)候才訪問(wèn)name
let name1 = zhangsan.student?.name
// 另外`var student:Student?`,如果student是為`nil`,那么返回的name也是可空類型的,雖然在定義的時(shí)是`var name = "xxx"`
if name1 == nil {
print("zhangsan --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字")
} else {
print("zhangsan --- \(name1)")
}
let lisi = Person()
// 注意,只要有作實(shí)例化操作,那么lisi.student就不是為`nil`
lisi.student = Student()
let name2 = lisi.student?.name
if name2 == nil {
print("lisi --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字")
} else {
print("lisi --- \(name2!)")
輸出結(jié)果:
zhangsan --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字
lisi --- xxx
二、為可空鏈?zhǔn)秸{(diào)用定義模型類
通過(guò)使用可空鏈?zhǔn)秸{(diào)用可以調(diào)用多層屬性、方法、下標(biāo)。這可以通過(guò)各種模型向下訪問(wèn)各種子屬性,并且判斷能否訪問(wèn)子屬性中的屬性、方法、下標(biāo)。
// 人類
class Person {
// 一個(gè)人,不一定就有房子,即是可空的
var residence:Residence?
}
// 房子類
class Residence {
// 房間數(shù)組
var rooms = [Room]()
// 房間個(gè)數(shù)
var numberOfRoos:Int {
return rooms.count
}
// 下標(biāo)腳本,下標(biāo)快捷訪問(wèn)數(shù)組rooms
subscript(i:Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
// 打印房子中房間數(shù)量函數(shù)
func printNumberOfRooms() {
print("這個(gè)房子有\(zhòng)(numberOfRoos)個(gè)房間")
}
}
// 房間類
class Room {
let name:String
init(name:String) {
self.name = name
}
}
// 地址類
class Address {
// 房子建造者的名字,可空屬性
var buildingName:String?
// 房子建造者的聯(lián)系方法,可空屬性
var buildingTell:String?
// 房子所在位置,可空屬性
var street:String?
// 房子建造的相關(guān)信息
func buildingIdentifier() -> String? {
if buildingName != nil {
return buildingName
} else if buildingTell != nil {
return buildingTell
} else {
return nil
}
}
}
三、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)屬性
根據(jù)上面創(chuàng)建的類,實(shí)例操作來(lái)進(jìn)行訪問(wèn)對(duì)應(yīng)屬性:
// 實(shí)例化Person
let endEvent = Person()
// 注意Residence為`nil`,那么可空鏈?zhǔn)秸{(diào)用失敗
if let roomCount = endEvent.residence?.numberOfRoos {
print("EndEvent房子中有\(zhòng)(roomCount)個(gè)房間")
} else {
print("Residence沒(méi)有進(jìn)行實(shí)例化操作")
}
// 實(shí)例化Address
let guangzhou = Address()
guangzhou.buildingName = "xxx"
guangzhou.street = "體育中心"
guangzhou.buildingTell = "xxx-xxx-xxx"
// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)設(shè)置屬性值
// 但是此時(shí)設(shè)置是失敗的,因?yàn)閑ndEvent.residence為空
endEvent.residence?.address = guangzhou
// 打印是為`nil`
print(endEvent.residence?.address?.street)
輸出結(jié)果:
Residence沒(méi)有進(jìn)行實(shí)例化操作
nil
從上可以看出,使用可空鏈?zhǔn)秸{(diào)用的好處,在于雖然
endEvent.residence為nil,在設(shè)置address屬性時(shí)程序不會(huì)崩潰,只是設(shè)置會(huì)失敗。
四、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用方法
在Residence類中,有一個(gè)printNumberOfRooms方法,是打印房子中房間數(shù)量的函數(shù)。對(duì)于這個(gè)函數(shù),其返回值類型是Void。但是如果在可空值上,通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用此方法,返回值類型是Void?,而不是Void,因?yàn)橥ㄟ^(guò)可空鏈?zhǔn)秸{(diào)用得到的返回值是nil。
// 如果有實(shí)例操作,那么就會(huì)是調(diào)用成功,否則是調(diào)用失敗
//endEvent.residence = Residence()
// 通過(guò)返回值是否為`nil`可以判斷調(diào)用是否成功
if endEvent.residence?.printNumberOfRooms() != nil {
print("調(diào)用成功")
} else {
print("調(diào)用失敗")
}
五、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)下標(biāo)
通過(guò)可空鏈?zhǔn)秸{(diào)用,可以用下標(biāo)來(lái)對(duì)可空值進(jìn)行讀寫(xiě),并可以判斷下標(biāo)調(diào)用是否成功。
注意: 當(dāng)通過(guò)可空鏈?zhǔn)秸{(diào)用訪問(wèn)可空值的下標(biāo)時(shí),應(yīng)該將問(wèn)號(hào)放在下標(biāo)方括號(hào)的前面而不是后面??煽真?zhǔn)秸{(diào)用的問(wèn)號(hào)一般直接跟在可空表達(dá)式的后面。
endEvent.residence = Residence()
// 追加操作
endEvent.residence?.rooms.append(Room(name: "房間1"))
endEvent.residence?.rooms.append(Room(name: "房間2"))
endEvent.residence?.rooms.append(Room(name: "房間3"))
// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)下標(biāo)
if let firstRoomName = endEvent.residence?[0].name {
print("第一個(gè)房間的名字是\(firstRoomName)")
} else {
print("沒(méi)有對(duì)應(yīng)房間")
}
輸出結(jié)果:
第一個(gè)房間的名字:房間1
// 訪問(wèn)可空類型的下標(biāo)
// 字典類型
var dict = ["name":["張三","李四","王五"],
"age":[18, 20, 23]]
// 用可空鏈?zhǔn)秸{(diào)用,把"name"數(shù)組第一個(gè)元素設(shè)置為"趙六"
dict["name"]?[0] = "趙六"
// 將"age"數(shù)組第一個(gè)元素設(shè)置為25
dict["age"]?[0] = 25
// 將"test"數(shù)組第一個(gè)元素設(shè)置為"測(cè)試",但是字典中不存在關(guān)鍵字為"test"的,所以這個(gè)是調(diào)用失敗
dict["test"]?[0] = "測(cè)試"
print(dict)
輸出結(jié)果:
["age": [25, 20, 23], "name": [趙六, 李四, 王五]]
六、多層鏈接
可以通過(guò)多個(gè)鏈接多個(gè)可空鏈?zhǔn)秸{(diào)用來(lái)向下訪問(wèn)屬性、方法、下標(biāo)。但是多層可空鏈?zhǔn)秸{(diào)用不會(huì)添加返回值的可空性,即是:
?- 如果訪問(wèn)的值不是可空的,通過(guò)可空鏈?zhǔn)秸{(diào)用將會(huì)可以放可空值。
?- 如果訪問(wèn)的值已經(jīng)是可空的,通過(guò)可空鏈?zhǔn)秸{(diào)用不會(huì)變得"更"可空。
// 多層可空鏈?zhǔn)秸{(diào)用
let endEvent = Person()
// 實(shí)例化Residence(如果沒(méi)有下實(shí)例化,那么可空鏈?zhǔn)綄?huì)調(diào)用失敗)
endEvent.residence = Residence()
// 實(shí)例化Address
let guangzhou = Address()
guangzhou.buildingName = "xxx"
guangzhou.street = "廣州 -- 體育中心"
guangzhou.buildingTell = "xxx-xxx-xxx"
endEvent.residence?.address = guangzhou
// `street`屬性是`String?`,endEvent.residence?.address?.street返回值是`String?`,即是已經(jīng)進(jìn)行了兩次可空鏈?zhǔn)秸{(diào)用
if let myStreet = endEvent.residence?.address?.street {
print("endEvent的住房地址: \(myStreet)")
} else {
print("endEvent沒(méi)有房子")
}
輸出結(jié)果:
endEvent的住房地址: 廣州 -- 體育中心
七、對(duì)返回可空值的函數(shù)進(jìn)行鏈接
除了通過(guò)可空鏈?zhǔn)秸{(diào)用獲取可空屬性值,也還可以通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用返回值為可空的方法。
// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用`Address`中的`buildingIdentifier()`方法,返回類型是`String?`。
if let myBuildingIdentifier = endEvent.residence?.address?.buildingIdentifier() {
print("建造信息: \(myBuildingIdentifier)")
}
注意: 在方法的括號(hào)后面加上問(wèn)號(hào),是因?yàn)?code>buildingIdentifier()返回值是可空值,而不是方法本身是可空的?。?!