在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 可選鏈?zhǔn)秸{(diào)用
特性(Attributes)
Access Control 權(quán)限控制的黑與白
基本運(yùn)算符(Basic Operators)
基礎(chǔ)部分(The Basics)
閉包(Closures)
擴(kuò)展
泛型參數(shù)(Generic Parameters and Arguments)
訪問控制和 protected
語(yǔ)句(Statements)
模式(Patterns)
WWDC 里面的那個(gè)“大炮打氣球”
關(guān)于語(yǔ)言參考(About the Language Reference)
語(yǔ)法總結(jié)(Summary of the Grammar)
嵌套類型
類型(Types)
Swift 初見(A Swift Tour)
泛型
枚舉(Enumerations)
高級(jí)運(yùn)算符
繼承
析構(gòu)過程
關(guān)于 Swift(About Swift)
訪問控制
類和結(jié)構(gòu)體
內(nèi)存安全
Swift 與 C 語(yǔ)言指針友好合作
協(xié)議
屬性(Properties)
可選類型完美解決占位問題
錯(cuò)誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動(dòng)引用計(jì)數(shù)
Swift 里的值類型與引用類型
表達(dá)式(Expressions)
Swift 文檔修訂歷史
造個(gè)類型不是夢(mèng)-白話 Swift 類型創(chuàng)建
歡迎使用 Swift
詞法結(jié)構(gòu)(Lexical Structure)
集合類型(Collection Types)
下標(biāo)
方法(Methods)
可選鏈?zhǔn)秸{(diào)用
版本兼容性
類型轉(zhuǎn)換
構(gòu)造過程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

可選鏈?zhǔn)秸{(diào)用


1.0 翻譯:Jasonbroker 校對(duì):numbbbbb, stanzhai

2.0 翻譯+校對(duì):lyojo

2.1 校對(duì):shanks,2015-10-31

2.2 翻譯+校對(duì):SketchK 2016-05-15

3.0.1,shanks,2016-11-13

4.0 校對(duì):kemchenj 2017-09-21

4.1 翻譯+校對(duì):mylittleswift

本頁(yè)包含內(nèi)容:

可選鏈?zhǔn)秸{(diào)用是一種可以在當(dāng)前值可能為 nil 的可選值上請(qǐng)求和調(diào)用屬性、方法及下標(biāo)的方法。如果可選值有值,那么調(diào)用就會(huì)成功;如果可選值是 nil,那么調(diào)用將返回 nil。多個(gè)調(diào)用可以連接在一起形成一個(gè)調(diào)用鏈,如果其中任何一個(gè)節(jié)點(diǎn)為 nil,整個(gè)調(diào)用鏈都會(huì)失敗,即返回 nil。

注意

Swift 的可選鏈?zhǔn)秸{(diào)用和 Objective-C 中向 nil 發(fā)送消息有些相像,但是 Swift 的可選鏈?zhǔn)秸{(diào)用可以應(yīng)用于任意類型,并且能檢查調(diào)用是否成功。

使用可選鏈?zhǔn)秸{(diào)用代替強(qiáng)制展開

通過在想調(diào)用的屬性、方法,或下標(biāo)的可選值后面放一個(gè)問號(hào)(?),可以定義一個(gè)可選鏈。這一點(diǎn)很像在可選值后面放一個(gè)嘆號(hào)(!)來強(qiáng)制展開它的值。它們的主要區(qū)別在于當(dāng)可選值為空時(shí)可選鏈?zhǔn)秸{(diào)用只會(huì)調(diào)用失敗,然而強(qiáng)制展開將會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。

為了反映可選鏈?zhǔn)秸{(diào)用可以在空值(nil)上調(diào)用的事實(shí),不論這個(gè)調(diào)用的屬性、方法及下標(biāo)返回的值是不是可選值,它的返回結(jié)果都是一個(gè)可選值。你可以利用這個(gè)返回值來判斷你的可選鏈?zhǔn)秸{(diào)用是否調(diào)用成功,如果調(diào)用有返回值則說明調(diào)用成功,返回 nil 則說明調(diào)用失敗。

特別地,可選鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可選值。例如,使用可選鏈?zhǔn)秸{(diào)用訪問屬性,當(dāng)可選鏈?zhǔn)秸{(diào)用成功時(shí),如果屬性原本的返回結(jié)果是 Int 類型,則會(huì)變?yōu)?Int? 類型。

下面幾段代碼將解釋可選鏈?zhǔn)秸{(diào)用和強(qiáng)制展開的不同。

首先定義兩個(gè)類 PersonResidence

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

Residence 有一個(gè) Int 類型的屬性 numberOfRooms,其默認(rèn)值為 1Person 具有一個(gè)可選的 residence 屬性,其類型為 Residence?

假如你創(chuàng)建了一個(gè)新的 Person 實(shí)例,它的 residence 屬性由于是是可選型而將初始化為 nil,在下面的代碼中,john 有一個(gè)值為 nilresidence 屬性:

let john = Person()

如果使用嘆號(hào)(!)強(qiáng)制展開獲得這個(gè) johnresidence 屬性中的 numberOfRooms 值,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤,因?yàn)檫@時(shí) residence 沒有可以展開的值:

let roomCount = john.residence!.numberOfRooms
// 這會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤

john.residence 為非 nil 值的時(shí)候,上面的調(diào)用會(huì)成功,并且把 roomCount 設(shè)置為 Int 類型的房間數(shù)量。正如上面提到的,當(dāng) residencenil 的時(shí)候上面這段代碼會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。

可選鏈?zhǔn)秸{(diào)用提供了另一種訪問 numberOfRooms 的方式,使用問號(hào)(?)來替代原來的嘆號(hào)(!):

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 后面添加問號(hào)之后,Swift 就會(huì)在 residence 不為 nil 的情況下訪問 numberOfRooms。

因?yàn)樵L問 numberOfRooms 有可能失敗,可選鏈?zhǔn)秸{(diào)用會(huì)返回 Int? 類型,或稱為“可選的 Int”。如上例所示,當(dāng) residencenil 的時(shí)候,可選的 Int 將會(huì)為 nil,表明無(wú)法訪問 numberOfRooms。訪問成功時(shí),可選的 Int 值會(huì)通過可選綁定展開,并賦值給非可選類型的 roomCount 常量。

要注意的是,即使 numberOfRooms 是非可選的 Int 時(shí),這一點(diǎn)也成立。只要使用可選鏈?zhǔn)秸{(diào)用就意味著 numberOfRooms 會(huì)返回一個(gè) Int? 而不是 Int

可以將一個(gè) Residence 的實(shí)例賦給 john.residence,這樣它就不再是 nil 了:

john.residence = Residence()

john.residence 現(xiàn)在包含一個(gè)實(shí)際的 Residence 實(shí)例,而不再是 nil。如果你試圖使用先前的可選鏈?zhǔn)秸{(diào)用訪問 numberOfRooms,它現(xiàn)在將返回值為 1Int? 類型的值:

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).”

為可選鏈?zhǔn)秸{(diào)用定義模型類

通過使用可選鏈?zhǔn)秸{(diào)用可以調(diào)用多層屬性、方法和下標(biāo)。這樣可以在復(fù)雜的模型中向下訪問各種子屬性,并且判斷能否訪問子屬性的屬性、方法或下標(biāo)。

下面這段代碼定義了四個(gè)模型類,這些例子包括多層可選鏈?zhǔn)秸{(diào)用。為了方便說明,在 PersonResidence 的基礎(chǔ)上增加了 Room 類和 Address 類,以及相關(guān)的屬性、方法以及下標(biāo)。

Person 類的定義基本保持不變:

class Person {
    var residence: Residence?
}

Residence 類比之前復(fù)雜些,增加了一個(gè)名為 rooms 的變量屬性,該屬性被初始化為 [Room] 類型的空數(shù)組:

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("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}

現(xiàn)在 Residence 有了一個(gè)存儲(chǔ) Room 實(shí)例的數(shù)組,numberOfRooms 屬性被實(shí)現(xiàn)為計(jì)算型屬性,而不是存儲(chǔ)型屬性。numberOfRooms 屬性簡(jiǎn)單地返回 rooms 數(shù)組的 count 屬性的值。

Residence 還提供了訪問 rooms 數(shù)組的快捷方式,即提供可讀寫的下標(biāo)來訪問 rooms 數(shù)組中指定位置的元素。

此外,Residence 還提供了 printNumberOfRooms 方法,這個(gè)方法的作用是打印 numberOfRooms 的值。

最后,Residence 還定義了一個(gè)可選屬性 address,其類型為 Address?。Address 類的定義在下面會(huì)說明。

Room 類是一個(gè)簡(jiǎn)單類,其實(shí)例被存儲(chǔ)在 rooms 數(shù)組中。該類只包含一個(gè)屬性 name,以及一個(gè)用于將該屬性設(shè)置為適當(dāng)?shù)姆块g名的初始化函數(shù):

class Room {
    let name: String
    init(name: String) { self.name = name }
}

最后一個(gè)類是 Address,這個(gè)類有三個(gè) String? 類型的可選屬性。buildingName 以及 buildingNumber 屬性分別表示某個(gè)大廈的名稱和號(hào)碼,第三個(gè)屬性 street 表示大廈所在街道的名稱:

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if buildingName != nil {
            return buildingName
        } else if buildingNumber != nil && street != nil {
            return "\(buildingNumber) \(street)"
        } else {
            return nil
        }
    }
}

Address 類提供了 buildingIdentifier() 方法,返回值為 String?。 如果 buildingName 有值則返回 buildingName?;蛘撸绻?buildingNumberstreet 均有值則返回 buildingNumber。否則,返回 nil。

通過可選鏈?zhǔn)秸{(diào)用訪問屬性

正如使用可選鏈?zhǔn)秸{(diào)用代替強(qiáng)制展開中所述,可以通過可選鏈?zhǔn)秸{(diào)用在一個(gè)可選值上訪問它的屬性,并判斷訪問是否成功。

下面的代碼創(chuàng)建了一個(gè) Person 實(shí)例,然后像之前一樣,嘗試訪問 numberOfRooms 屬性:

let john = Person()
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.”

因?yàn)?john.residencenil,所以這個(gè)可選鏈?zhǔn)秸{(diào)用依舊會(huì)像先前一樣失敗。

還可以通過可選鏈?zhǔn)秸{(diào)用來設(shè)置屬性值:

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress

在這個(gè)例子中,通過 john.residence 來設(shè)定 address 屬性也會(huì)失敗,因?yàn)?john.residence 當(dāng)前為 nil

上面代碼中的賦值過程是可選鏈?zhǔn)秸{(diào)用的一部分,這意味著可選鏈?zhǔn)秸{(diào)用失敗時(shí),等號(hào)右側(cè)的代碼不會(huì)被執(zhí)行。對(duì)于上面的代碼來說,很難驗(yàn)證這一點(diǎn),因?yàn)橄襁@樣賦值一個(gè)常量沒有任何副作用。下面的代碼完成了同樣的事情,但是它使用一個(gè)函數(shù)來創(chuàng)建 Address 實(shí)例,然后將該實(shí)例返回用于賦值。該函數(shù)會(huì)在返回前打印“Function was called”,這使你能驗(yàn)證等號(hào)右側(cè)的代碼是否被執(zhí)行。

func createAddress() -> Address {
    print("Function was called.")

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"

    return someAddress
}
john.residence?.address = createAddress()

沒有任何打印消息,可以看出 createAddress() 函數(shù)并未被執(zhí)行。

通過可選鏈?zhǔn)秸{(diào)用來調(diào)用方法

可以通過可選鏈?zhǔn)秸{(diào)用來調(diào)用方法,并判斷是否調(diào)用成功,即使這個(gè)方法沒有返回值。

Residence 類中的 printNumberOfRooms() 方法打印當(dāng)前的 numberOfRooms 值,如下所示:

func printNumberOfRooms() {
    print("The number of rooms is \(numberOfRooms)")
}

這個(gè)方法沒有返回值。然而,沒有返回值的方法具有隱式的返回類型 Void,如無(wú)返回值函數(shù)中所述。這意味著沒有返回值的方法也會(huì)返回 (),或者說空的元組。

如果在可選值上通過可選鏈?zhǔn)秸{(diào)用來調(diào)用這個(gè)方法,該方法的返回類型會(huì)是 Void?,而不是 Void,因?yàn)橥ㄟ^可選鏈?zhǔn)秸{(diào)用得到的返回值都是可選的。這樣我們就可以使用 if 語(yǔ)句來判斷能否成功調(diào)用 printNumberOfRooms() 方法,即使方法本身沒有定義返回值。通過判斷返回值是否為 nil 可以判斷調(diào)用是否成功:

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// 打印 “It was not possible to print the number of rooms.”

同樣的,可以據(jù)此判斷通過可選鏈?zhǔn)秸{(diào)用為屬性賦值是否成功。在上面的通過可選鏈?zhǔn)秸{(diào)用訪問屬性的例子中,我們嘗試給 john.residence 中的 address 屬性賦值,即使 residencenil。通過可選鏈?zhǔn)秸{(diào)用給屬性賦值會(huì)返回 Void?,通過判斷返回值是否為 nil 就可以知道賦值是否成功:

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}
// 打印 “It was not possible to set the address.”

通過可選鏈?zhǔn)秸{(diào)用訪問下標(biāo)

通過可選鏈?zhǔn)秸{(diào)用,我們可以在一個(gè)可選值上訪問下標(biāo),并且判斷下標(biāo)調(diào)用是否成功。

注意

通過可選鏈?zhǔn)秸{(diào)用訪問可選值的下標(biāo)時(shí),應(yīng)該將問號(hào)放在下標(biāo)方括號(hào)的前面而不是后面。可選鏈?zhǔn)秸{(diào)用的問號(hào)一般直接跟在可選表達(dá)式的后面。

下面這個(gè)例子用下標(biāo)訪問 john.residence 屬性存儲(chǔ)的 Residence 實(shí)例的 rooms 數(shù)組中的第一個(gè)房間的名稱,因?yàn)?john.residencenil,所以下標(biāo)調(diào)用失敗了:

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// 打印 “Unable to retrieve the first room name.”

在這個(gè)例子中,問號(hào)直接放在 john.residence 的后面,并且在方括號(hào)的前面,因?yàn)?john.residence 是可選值。

類似的,可以通過下標(biāo),用可選鏈?zhǔn)秸{(diào)用來賦值:

john.residence?[0] = Room(name: "Bathroom")

這次賦值同樣會(huì)失敗,因?yàn)?residence 目前是 nil。

如果你創(chuàng)建一個(gè) Residence 實(shí)例,并為其 rooms 數(shù)組添加一些 Room 實(shí)例,然后將 Residence 實(shí)例賦值給 john.residence,那就可以通過可選鏈和下標(biāo)來訪問數(shù)組中的元素:

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// 打印 “The first room name is Living Room.”

訪問可選類型的下標(biāo)

如果下標(biāo)返回可選類型值,比如 Swift 中 Dictionary 類型的鍵的下標(biāo),可以在下標(biāo)的結(jié)尾括號(hào)后面放一個(gè)問號(hào)來在其可選返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用:

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// "Dave" 數(shù)組現(xiàn)在是 [91, 82, 84],"Bev" 數(shù)組現(xiàn)在是 [80, 94, 81]

上面的例子中定義了一個(gè) testScores 數(shù)組,包含了兩個(gè)鍵值對(duì),把 String 類型的鍵映射到一個(gè) Int 值的數(shù)組。這個(gè)例子用可選鏈?zhǔn)秸{(diào)用把 "Dave" 數(shù)組中第一個(gè)元素設(shè)為 91,把 "Bev" 數(shù)組的第一個(gè)元素 +1,然后嘗試把 "Brian" 數(shù)組中的第一個(gè)元素設(shè)為 72。前兩個(gè)調(diào)用成功,因?yàn)?testScores 字典中包含 "Dave""Bev" 這兩個(gè)鍵。但是 testScores 字典中沒有 "Brian" 這個(gè)鍵,所以第三個(gè)調(diào)用失敗。

連接多層可選鏈?zhǔn)秸{(diào)用

可以通過連接多個(gè)可選鏈?zhǔn)秸{(diào)用在更深的模型層級(jí)中訪問屬性、方法以及下標(biāo)。然而,多層可選鏈?zhǔn)秸{(diào)用不會(huì)增加返回值的可選層級(jí)。

也就是說:

  • 如果你訪問的值不是可選的,可選鏈?zhǔn)秸{(diào)用將會(huì)返回可選值。
  • 如果你訪問的值就是可選的,可選鏈?zhǔn)秸{(diào)用不會(huì)讓可選返回值變得“更可選”。

因此:

  • 通過可選鏈?zhǔn)秸{(diào)用訪問一個(gè) Int 值,將會(huì)返回 Int?,無(wú)論使用了多少層可選鏈?zhǔn)秸{(diào)用。
  • 類似的,通過可選鏈?zhǔn)秸{(diào)用訪問 Int? 值,依舊會(huì)返回 Int? 值,并不會(huì)返回 Int??。

下面的例子嘗試訪問 john 中的 residence 屬性中的 address 屬性中的 street 屬性。這里使用了兩層可選鏈?zhǔn)秸{(diào)用,residence 以及 address 都是可選值:

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// 打印 “Unable to retrieve the address.”

john.residence 現(xiàn)在包含一個(gè)有效的 Residence 實(shí)例。然而,john.residence.address 的值當(dāng)前為 nil。因此,調(diào)用 john.residence?.address?.street 會(huì)失敗。

需要注意的是,上面的例子中,street 的屬性為 String?。john.residence?.address?.street 的返回值也依然是 String?,即使已經(jīng)使用了兩層可選鏈?zhǔn)秸{(diào)用。

如果為 john.residence.address 賦值一個(gè) Address 實(shí)例,并且為 address 中的 street 屬性設(shè)置一個(gè)有效值,我們就能過通過可選鏈?zhǔn)秸{(diào)用來訪問 street 屬性:

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// 打印 “John's street name is Laurel Street.”

在上面的例子中,因?yàn)?john.residence 包含一個(gè)有效的 Address 實(shí)例,所以對(duì) john.residenceaddress 屬性賦值將會(huì)成功。

在方法的可選返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用

上面的例子展示了如何在一個(gè)可選值上通過可選鏈?zhǔn)秸{(diào)用來獲取它的屬性值。我們還可以在一個(gè)可選值上通過可選鏈?zhǔn)秸{(diào)用來調(diào)用方法,并且可以根據(jù)需要繼續(xù)在方法的可選返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用。

在下面的例子中,通過可選鏈?zhǔn)秸{(diào)用來調(diào)用 AddressbuildingIdentifier() 方法。這個(gè)方法返回 String? 類型的值。如上所述,通過可選鏈?zhǔn)秸{(diào)用來調(diào)用該方法,最終的返回值依舊會(huì)是 String? 類型:

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \(buildingIdentifier).")
}
// 打印 “John's building identifier is The Larches.”

如果要在該方法的返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用,在方法的圓括號(hào)后面加上問號(hào)即可:

if let beginsWithThe =
    john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
        if beginsWithThe {
            print("John's building identifier begins with \"The\".")
        } else {
            print("John's building identifier does not begin with \"The\".")
        }
}
// 打印 “John's building identifier begins with "The".”

注意

在上面的例子中,在方法的圓括號(hào)后面加上問號(hào)是因?yàn)槟阋?buildingIdentifier() 方法的可選返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用,而不是 buildingIdentifier() 方法本身。