第二十四章 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>

1. 可選鏈可替代強(qiáng)制解析

通過在屬性突雪、方法、或下標(biāo)腳本的可選值后面放一個問號?涡贱,即可定義一個可選鏈咏删。

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

// 將導(dǎo)致運(yùn)行時錯誤
let roomCount = john.residence!.numberOfRooms // fatal error: unexpectedly found nil while unwrapping an Optional value

想使用感嘆號!強(qiáng)制解析獲得這個人residence屬性numberOfRooms屬性值,將會引發(fā)運(yùn)行時錯誤问词,因?yàn)檫@時沒有可以供解析的residence值督函。

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("不能查看房間號")
}

// 不能查看房間號

因?yàn)檫@種嘗試獲得numberOfRooms的操作有可能失敗辰狡,可選鏈會返回Int?類型值。當(dāng)residence是空的時候(上例)垄分,選擇Int將會為空宛篇,因此會出現(xiàn)無法訪問numberOfRooms的情況。

要注意的是薄湿,即使numberOfRooms是非可選Int(Int?)時這一點(diǎn)也成立叫倍。只要是通過可選鏈的請求就意味著最后numberOfRooms總是返回一個Int?而不是Int。

2. 為可選鏈定義模型類

你可以使用可選鏈來多層調(diào)用屬性豺瘤,方法吆倦,和下標(biāo)腳本。這讓你可以利用它們之間的復(fù)雜模型來獲取更底層的屬性炉奴,并檢查是否可以成功獲取此類底層屬性逼庞。

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("無法輸出房間號")
}

// 無法輸出房間號

使用if語句來檢查是否能成功調(diào)用printNumberOfRooms方法:如果方法通過可選鏈調(diào)用成功璧南,printNumberOfRooms的隱式返回值將會是Void,如果沒有成功师逸,將返回nil司倚。

3. 使用可選鏈調(diào)用下標(biāo)腳本

你可以使用可選鏈來嘗試從下標(biāo)腳本獲取值并檢查下標(biāo)腳本的調(diào)用是否成功,然而篓像,你不能通過可選鏈來設(shè)置下標(biāo)腳本动知。

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("無法檢索到房間")
}

// 無法檢索到房間

在下標(biāo)腳本調(diào)用中可選鏈的問號直接跟在 circname.print的后面员辩,在下標(biāo)腳本括號的前面盒粮,因?yàn)?code>circname.print是可選鏈試圖獲得的可選值。

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("無法檢索到地址。 ")
}

// John 所在的街道是 Laurel Street宋税。

4. 通過可選鏈接調(diào)用來訪問下標(biāo)

通過可選鏈接調(diào)用摊崭,我們可以用下標(biāo)來對可選值進(jìn)行讀取或?qū)懭耄⑶遗袛嘞聵?biāo)調(diào)用是否成功杰赛。

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("無法檢索到房間")
}

// 第一個房間名為客廳

5. 訪問可選類型的下標(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]

這個例子用可選鏈接調(diào)用把"Dave"數(shù)組中第一個元素設(shè)為91,把"Bev"數(shù)組的第一個元素+1瓶珊,然后嘗試把"Brian"數(shù)組中的第一個元素設(shè)為72啸箫。前兩個調(diào)用是成功的,因?yàn)檫@兩個key存在伞芹。但是key"Brian"在字典中不存在忘苛,所以第三個調(diào)用失敗。

6. 連接多層鏈接

你可以將多層可選鏈連接在一起唱较,可以掘取模型內(nèi)更下層的屬性方法和下標(biāo)腳本扎唾。然而多層可選鏈不能再添加比已經(jīng)返回的可選值更多的層。

如果你試圖通過可選鏈獲得Int值南缓,不論使用了多少層鏈接返回的總是Int?胸遇。 相似的,如果你試圖通過可選鏈獲得Int?值汉形,不論使用了多少層鏈接返回的總是Int?纸镊。

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("不能檢索地址")
}

// 不能檢索地址
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("無法檢索房間")
}

// 第一個房間是客廳

7. 對返回可選值的函數(shù)進(jìn)行鏈接

我們還可以通過可選鏈接來調(diào)用返回可空值的方法,并且可以繼續(xù)對可選值進(jìn)行鏈接逗威。

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é)果為:

// 未指定房間號

8. 備選值

當(dāng)我們企圖獲取可選值的值卻失敗的時候,可以使用??來處理無值的情況

let a: String? = "a"
let b = a ?? "b"

let c: String? = nil
let d = c ?? "d"

print(b) // "a"
print(d) //  "d"

a定義為可選值凯旭,但實(shí)際是有值的概耻,故b為"a",c無值罐呼,故選取備選項"d"鞠柄。

在保證可選值一定有值時,可以使用!來獲取

let s = a!
print(s) // "a"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嫉柴,一起剝皮案震驚了整個濱河市春锋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌差凹,老刑警劉巖期奔,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異危尿,居然都是意外死亡呐萌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門谊娇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肺孤,“玉大人,你說我怎么就攤上這事济欢≡拢” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵法褥,是天一觀的道長茫叭。 經(jīng)常有香客問我,道長半等,這世上最難降的妖魔是什么揍愁? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮杀饵,結(jié)果婚禮上莽囤,老公的妹妹穿的比我還像新娘。我一直安慰自己切距,他們只是感情好朽缎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般话肖。 火紅的嫁衣襯著肌膚如雪北秽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天狼牺,我揣著相機(jī)與錄音,去河邊找鬼礼患。 笑死是钥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缅叠。 我是一名探鬼主播悄泥,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肤粱!你這毒婦竟也來了弹囚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤领曼,失蹤者是張志新(化名)和其女友劉穎鸥鹉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庶骄,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毁渗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了单刁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灸异。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖羔飞,靈堂內(nèi)的尸體忽然破棺而出肺樟,到底是詐尸還是另有隱情,我是刑警寧澤逻淌,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布么伯,位于F島的核電站,受9級特大地震影響卡儒,放射性物質(zhì)發(fā)生泄漏蹦狂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一朋贬、第九天 我趴在偏房一處隱蔽的房頂上張望凯楔。 院中可真熱鬧,春花似錦锦募、人聲如沸摆屯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虐骑。三九已至准验,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廷没,已是汗流浹背糊饱。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颠黎,地道東北人另锋。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像狭归,于是被迫代替她去往敵國和親夭坪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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