Swift可選鏈式呼叫

小豬熊發表於2017-12-13

可選鏈式呼叫是一種可以在當前值可能為nil的可選值上請求和呼叫屬性、方法及下標的方法。如果可選值有值,那麼呼叫就會成功;如果可選值是nil,那麼呼叫將返回nil。多個呼叫可以連線在一起形成一個呼叫鏈,如果其中任何一個節點為nil,整個呼叫鏈都會失敗,即返回nil

注意 Swift 的可選鏈式呼叫和 Objective-C 中向nil傳送訊息有些相像,但是 Swift 的可選鏈式呼叫可以應用於任意型別,並且能檢查呼叫是否成功。

使用可選鏈式呼叫代替強制展開

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

特別地,可選鏈式呼叫的返回結果與原本的返回結果具有相同的型別,但是被包裝成了一個可選值。例如,使用可選鏈式呼叫訪問屬性,當可選鏈式呼叫成功時,如果屬性原本的返回結果是Int型別,則會變為Int?型別。

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
複製程式碼

可選鏈式呼叫提供了另一種訪問numberOfRooms的方式,使用問號(?)來替代原來的歎號(!):

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後面新增問號之後,Swift 就會在residence不為nil的情況下訪問numberOfRooms

為可選鏈式呼叫定義模型類

通過使用可選鏈式呼叫可以呼叫多層屬性、方法和下標。這樣可以在複雜的模型中向下訪問各種子屬性,並且判斷能否訪問子屬性的屬性、方法或下標。

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("The number of rooms is \(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 && street != nil {
            return "\(buildingNumber) \(street)"
        } else {
            return 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.")
}
// 列印 “Unable to retrieve the number of rooms.”
複製程式碼
通過可選鏈式呼叫呼叫方法

可以通過可選鏈式呼叫來呼叫方法,並判斷是否呼叫成功,即使這個方法沒有返回值。

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.”
複製程式碼

同樣的,可以據此判斷通過可選鏈式呼叫為屬性賦值是否成功。

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.”
複製程式碼
通過可選鏈式呼叫訪問下標

通過可選鏈式呼叫,我們可以在一個可選值上訪問下標,並且判斷下標呼叫是否成功。

注意 通過可選鏈式呼叫訪問可選值的下標時,應該將問號放在下標方括號的前面而不是後面。可選鏈式呼叫的問號一般直接跟在可選表示式的後面。

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.”
複製程式碼

類似的,可以通過下標,用可選鏈式呼叫來賦值:

john.residence?[0] = Room(name: "Bathroom")
複製程式碼

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

訪問可選型別的下標

如果下標返回可選型別值,比如 Swift 中Dictionary型別的鍵的下標,可以在下標的結尾括號後面放一個問號來在其可選返回值上進行可選鏈式呼叫:

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// "Dave" 陣列現在是 [91, 82, 84],"Bev" 陣列現在是 [80, 94, 81]
複製程式碼

上面的例子中定義了一個testScores陣列,包含了兩個鍵值對,把String型別的鍵對映到一個Int值的陣列。這個例子用可選鏈式呼叫把"Dave"陣列中第一個元素設為91,把"Bev"陣列的第一個元素+1,然後嘗試把"Brian"陣列中的第一個元素設為72。前兩個呼叫成功,因為testScores字典中包含"Dave""Bev"這兩個鍵。但是testScores字典中沒有"Brian"這個鍵,所以第三個呼叫失敗。

連線多層可選鏈式呼叫

可以通過連線多個可選鏈式呼叫在更深的模型層級中訪問屬性、方法以及下標。然而,多層可選鏈式呼叫不會增加返回值的可選層級。

也就是說:

  • 如果你訪問的值不是可選的,可選鏈式呼叫將會返回可選值。
  • 如果你訪問的值就是可選的,可選鏈式呼叫不會讓可選返回值變得“更可選”。

因此:

  • 通過可選鏈式呼叫訪問一個Int值,將會返回Int?,無論使用了多少層可選鏈式呼叫。
  • 類似的,通過可選鏈式呼叫訪問Int?值,依舊會返回Int?值,並不會返回Int??
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.”
複製程式碼
在方法的可選返回值上進行可選鏈式呼叫

如果要在該方法的返回值上進行可選鏈式呼叫,在方法的圓括號後面加上問號即可:

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".”
複製程式碼

注意 在上面的例子中,在方法的圓括號後面加上問號是因為你要在buildingIdentifier()方法的可選返回值上進行可選鏈式呼叫,而不是方法本身。

引自:http://www.piggybear.net/?p=699

相關文章