Swift 可選鏈

可選鏈(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é)果為:

未指定房間號

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陨溅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绍在,更是在濱河造成了極大的恐慌门扇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偿渡,死亡現(xiàn)場離奇詭異臼寄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)溜宽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門吉拳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坑质,你說我怎么就攤上這事合武×俟#” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵稼跳,是天一觀的道長盟庞。 經(jīng)常有香客問我,道長汤善,這世上最難降的妖魔是什么什猖? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮红淡,結(jié)果婚禮上不狮,老公的妹妹穿的比我還像新娘。我一直安慰自己在旱,他們只是感情好摇零,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桶蝎,像睡著了一般驻仅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上登渣,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天噪服,我揣著相機(jī)與錄音,去河邊找鬼胜茧。 笑死粘优,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呻顽。 我是一名探鬼主播雹顺,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芬位!你這毒婦竟也來了无拗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤昧碉,失蹤者是張志新(化名)和其女友劉穎英染,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體被饿,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡四康,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狭握。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闪金。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哎垦,到底是詐尸還是另有隱情囱嫩,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布漏设,位于F島的核電站墨闲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏郑口。R本人自食惡果不足惜鸳碧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犬性。 院中可真熱鬧瞻离,春花似錦、人聲如沸乒裆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缸兔。三九已至日裙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惰蜜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工受神, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抛猖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓鼻听,卻偏偏與公主長得像财著,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撑碴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 126.析構(gòu)器 在一個類實(shí)例銷毀前,一個析構(gòu)器會立即調(diào)用撑教。使用deinit 關(guān)鍵字來表示析構(gòu)器, 跟構(gòu)造器寫法類似...
    無灃閱讀 779評論 0 4
  • 介紹 可選鏈,英文叫Optional Chaining醉拓,是表示變量伟姐、屬性等值可為空,也就是值可能為nil亿卤。若有值愤兵,...
    jdong閱讀 359評論 0 0
  • 我是一塊石頭秆乳。不知從何時起,我有了意識,我只記得某一天屹堰,我睜開了眼睛肛冶,四周陰沉沉的,天空的西北角好像破了一個大洞扯键,...
    沉思的Panda閱讀 351評論 2 1
  • 1 有買過保險的人應(yīng)該都知道忧陪,買保險的意思是定期交付一定的金額給保險公司扣泊,成為保險公司的被保險人。當(dāng)被保險人受到一...
    沿海小生閱讀 3,516評論 17 50
  • Mileszhuer閱讀 276評論 0 6