進擊的雨燕-------------可空鏈式調用

來源:互聯網
上載者:User

標籤:

詳情轉自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html

可空鏈式調用(Optional Chaining)是一種可以請求和調用屬性、方法及下標的過程,它的可空性體現於請求或調用的目標當前可能為空白(nil)。如果可空的目標有值,那麼調用就會成功;如果選擇的目標為空白(nil),那麼這種調用將返回空(nil)。多個連續的調用可以被連結在一起形成一個調用鏈,如果其中任何一個節點為空白(nil)將導致整個鏈調用失敗。

注意: Swift 的可空鏈式調用和 Objective-C 中的訊息為空白有些相像,但是 Swift 可以使用在任意類型中,並且能夠檢查調用是否成功。

使用可空鏈式調用來強制展開

通過在想調用非空的屬性、方法、或下標的可空值(optional value)後面放一個問號,可以定義一個可空鏈。這一點很像在可空值後面放一個歎號(!)來強制展開其中值。它們的主要的區別在於當可空值為空白時可空鏈式只是調用失敗,然而強制展開將會觸發執行階段錯誤。

為了反映可空鏈式調用可以在Null 物件(nil)上調用,不論這個調用的屬性、方法、下標等返回的值是不是可空值,它的返回結果都是一個可空值。你可以利用這個傳回值來判斷你的可空鏈式調用是否調用成功,如果調用有傳回值則說明調用成功,返回nil則說明調用失敗。

特別地,可空鏈式調用的返回結果與原本的返回結果具有相同的類型,但是被封裝成了一個可空類型值。當可空鏈式調用成功時,一個本應該返回Int的類型的結果將會返回Int?類型。

下面幾段代碼將解釋可空鏈式調用和強制展開的不同。 首先定義兩個類PersonResidence

class Person {    var residence: Residence?}class Residence {    var numberOfRooms = 1}

Residence有一個Int類型的屬性numberOfRooms,其預設值為1。Person具有一個可空的residence屬性,其類型為Residence?

如果建立一個新的Person執行個體,因為它的residence屬性是可空的,john屬性將初始化為nil

let john = Person()

如果使用歎號(!)強制展開獲得這個johnresidence屬性中的numberOfRooms值,會觸發執行階段錯誤,因為這時沒有可以展開的residence

let roomCount = john.residence!.numberOfRooms// this triggers a runtime error

john.residence非空的時候,上面的調用成功,並且把roomCount設定為Int類型的房間數量。正如上面說到的,當residence為空白的時候上面這段代碼會觸發執行階段錯誤。

可空鏈式調用提供了一種另一種訪問numberOfRooms的方法,使用問號(?)來代替原來歎號(!)的位置:

if let roomCount = john.residence?.numberOfRooms {    print("John‘s residence has \(roomCount) room(s).")} else {    print("Unable to retrieve the number of rooms.")}// prints "Unable to retrieve the number of rooms."

residence後面添加問號之後,Swift就會在residence不為空白的情況下訪問numberOfRooms

因為訪問numberOfRooms有可能失敗,可空鏈式調用會返回Int?類型,或稱為“可空的Int”。如上例所示,當residencenil的時候,可空的Int將會為nil,表明無法訪問numberOfRooms

要注意的是,即使numberOfRooms是不可空的Int時,這一點也成立。只要是通過可空鏈式調用就意味著最後numberOfRooms返回一個Int?而不是Int

通過賦給john.residence一個Residence的執行個體變數:

john.residence = Residence()

這樣john.residence不為nil了。現在就可以正常訪問john.residence.numberOfRooms,其值為預設的1,類型為Int?

if let roomCount = john.residence?.numberOfRooms {    print("John‘s residence has \(roomCount) room(s).")} else {    print("Unable to retrieve the number of rooms.")}// prints "John‘s residence has 1 room(s)."

為可空鏈式調用定義模型類

通過使用可空鏈式調用可以調用多層屬性,方法,和下標。這樣可以通過各種模型向下訪問各種子屬性。並且判斷能否訪問子屬性的屬性,方法或下標。

下面這段代碼定義了四個模型類,這些例子包括多層可空鏈式調用。為了方便說明,在PersonResidence的基礎上增加了RoomAddress,以及相關的屬性,方法以及下標。

Person類定義基本保持不變:

class Person {    var residence: Residence?}

Residence類比之前複雜些,增加了一個Room類型的空數組room

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?}

現在Residence有了一個儲存Room類型的數組,numberOfRooms屬性需要計算,而不是作為單純的變數。計算後的numberOfRooms返回rooms數組的count屬性值。現在的Residence還提供訪問rooms數組的捷徑, 通過可讀寫的下標來訪問指定位置的數組元素。此外,還提供printNumberOfRooms方法,這個方法的作用就是輸出這個房子中房間的數量。最後,Residence定義了一個可空屬性address,其類型為Address?Address類的定義在下面會說明。

Room是一個簡單類,只包含一個屬性name,以及一個初始化函數:

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

最後一個類是Address,這個類有三個String?類型的可空屬性。buildingName以及buildingNumber屬性工作表示建築的名稱和號碼,用來表示某個特定的建築。第三個屬性工作表示建築所在街道的名稱:

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        }    }}

Address提供buildingIdentifier()方法,傳回值為String?。 如果buildingName不為空白則返回buildingName, 如果buildingNumber不為空白則返回buildingNumber。如果這兩個屬性都為空白則返回nil

通過可空鏈式調用訪問屬性

正如使用可空鏈式調用來強制展開中所述,可以通過可空鏈式調用訪問屬性的可空值,並且判斷訪問是否成功。

下面的代碼建立了一個Person執行個體,然後訪問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.")}// prints "Unable to retrieve the number of rooms."

因為john.residencenil,所以毫無疑問這個可空鏈式調用失敗。

通過可空鏈式調用來設定屬性值:

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

在這個例子中,通過john.residence來設定address屬性也是不行的,因為john.residencenil

通過可空鏈式調用來調用方法

可以通過可空鏈式調用來調用方法,並判斷是否調用成功,即使這個方法沒有傳回值。 Residence中的printNumberOfRooms()方法輸出當前的numberOfRooms值:

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

這個方法沒有傳回值。但是沒有傳回值的方法隱式返回Void類型,如無傳回值函數中所述。這意味著沒有傳回值的方法也會返回()或者空的元組。

如果在可空值上通過可空鏈式調用來調用這個方法,這個方法的傳回型別為Void?,而不是Void,因為通過可空鏈式調用得到的傳回值都是可空的。這樣我們就可以使用if語句來判斷能否成功調用printNumberOfRooms()方法,即使方法本身沒有定義傳回值。通過傳回值是否為nil可以判斷調用是否成功:

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.")}// prints "It was not possible to print the number of rooms."

同樣的,可以判斷通過可空鏈式調用來給屬性賦值是否成功。在上面的例子中,我們嘗試給john.residence中的address屬性賦值,即使residencenil。通過可空鏈式調用給屬性賦值會返回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.")}// prints "It was not possible to set the address."

通過可空鏈式調用來訪問下標

通過可空鏈式調用,我們可以用下標來對可空值進行讀取或寫入,並且判斷下標調用是否成功。

注意: 當通過可空鏈式調用訪問可空值的下標的時候,應該將問號放在下標方括弧的前面而不是後面。可空鏈式調用的問號一般直接跟在可空運算式的後面。

下面這個例子用下標訪問john.residencerooms數組中第一個房間的名稱,因為john.residencenil,所以下標調用毫無疑問失敗了:

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

在這個例子中,問號直接放在john.residence的後面,並且在方括弧的前面,因為john.residence是可空值。

類似的,可以通過下標,用可空鏈式調用來賦值:

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

這次賦值同樣會失敗,因為residence目前是nil

如果你建立一個Residence執行個體,添加一些Room執行個體並賦值給john.residence,那就可以通過可選鏈和下標來訪問數組中的元素:

let johnsHouse = Residence()johnsHouse.rooms.append(Room(name: "Living Room"))johnsHouse.rooms.append(Room(name: "Kitchen"))john.residence = johnsHouseif let firstRoomName = john.residence?[0].name {    print("The first room name is \(firstRoomName).")} else {    print("Unable to retrieve the first room name.")}// prints "The first room name is Living Room."

訪問可空類型的下標

如果下標返回可空類型值,比如Swift中Dictionarykey下標。可以在下標的閉合括弧後面放一個問號來連結下標的可空傳回值:

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]testScores["Dave"]?[0] = 91testScores["Bev"]?[0]++testScores["Brian"]?[0] = 72// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

上面的例子中定義了一個testScores數組,包含了兩個索引值對, 把String類型的key映射到一個整形數組。這個例子用可空鏈式調用把“Dave”數組中第一個元素設為91,把”Bev”數組的第一個元素+1,然後嘗試把”Brian”數組中的第一個元素設為72。前兩個調用是成功的,因為這兩個key存在。但是key“Brian”在字典中不存在,所以第三個調用失敗。

多層連結

可以通過多個連結多個可空鏈式調用來向下訪問屬性,方法以及下標。但是多層可空鏈式調用不會添加傳回值的可空性。

也就是說:

  • 如果你訪問的值不是可空的,通過可空鏈式調用將會放回可空值。
  • 如果你訪問的值已經是可空的,通過可空鏈式調用不會變得“更”可空。

因此:

  • 通過可空鏈式調用訪問一個Int值,將會返回Int?,不過進行了多少次可空鏈式調用。
  • 類似的,通過可空鏈式調用訪問Int?值,並不會變得更加可空。

下面的例子訪問john中的residence中的address中的street屬性。這裡使用了兩層可空鏈式調用,residence以及address,這兩個都是可空值。

if let johnsStreet = john.residence?.address?.street {    print("John‘s street name is \(johnsStreet).")} else {    print("Unable to retrieve the address.")}// prints "Unable to retrieve the address."

john.residence包含Residence執行個體,但是john.residence.addressnil。因此,不能訪問john.residence?.address?.street

需要注意的是,上面的例子中,street的屬性為String?john.residence?.address?.street的傳回值也依然是String?,即使已經進行了兩次可空的鏈式調用。

如果把john.residence.address指向一個執行個體,並且為address中的street屬性賦值,我們就能過通過可空鏈式調用來訪問street屬性。

let johnsAddress = Address()johnsAddress.buildingName = "The Larches"johnsAddress.street = "Laurel Street"john.residence?.address = johnsAddressif let johnsStreet = john.residence?.address?.street {    print("John‘s street name is \(johnsStreet).")} else {    print("Unable to retrieve the address.")}// prints "John‘s street name is Laurel Street."

在上面的例子中,因為john.residence是一個可用的Residence執行個體,所以對john.residenceaddress屬性賦值成功。

對返回可空值的函數進行連結

上面的例子說明了如何通過可空鏈式調用來擷取可空屬性值。我們還可以通過可空鏈式調用來調用返回可空值的方法,並且可以繼續對可空值進行連結。

在下面的例子中,通過可空鏈式調用來調用AddressbuildingIdentifier()方法。這個方法返回String?類型。正如上面所說,通過可空鏈式調用的方法的最終傳回值還是String?

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {    print("John‘s building identifier is \(buildingIdentifier).")}// prints "John‘s building identifier is The Larches."

如果要進一步對方法的傳回值進行可空鏈式調用,在方法buildingIdentifier()的圓括弧後面加上問號:

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\".")        }}// prints "John‘s building identifier begins with "The"."

注意: 在上面的例子中在,在方法的圓括弧後面加上問號是因為buildingIdentifier()的傳回值是可空值,而不是方法本身是可空的。

 

 

詳情轉自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html

進擊的雨燕-------------可空鏈式調用

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.