My Octopress Blog

A blogging framework for hackers.

Swift Optional Chaining

optinal chainging是一个查询和调用optional的property, method, subscript的过程。如果其中的optional没有nil,那么表达式求值成功。否则,失败。失败后返回nil。所以整个表达式返回值始终是optional类型。

以下是一些sample code, 先构造一些类

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(){
        println("The number of rooms is \(numberOfRooms)")
    }
}

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 buildNumber != nil {
            return buldingNumber
        } else {
            return nil
        }
    }
}

下面就是如何使用optional chain

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    println("john's residence has \(roomCount) rooms.")
}
// will print nothing

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

Residence里的方法printNumberOfRooms()没有返回值,但是这意味着该方法返回类型是Void,也就是返回值是(),也就是一个空的tuple. 在optional chain里,会返回Void?

if john.residence?.printNumberOfRoom() != nil {
    println("It was possible to print the number of rooms."
} 
// will print nothing

对于通过optional chain来设置属性也一样

if (john.residence?.address = someAddress) != nil {
    println("It was possible to set the address."
}
// will print nothing

通过optional chaining访问subscript

if let firstRoomName = john.residence?[0].name {
    println("the first room name is \(firstRoomName)")
}
// will print nothing

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 {
    println("the first room name is \(firstRoomName)")
}
// prints "the first room name is Living Room"

访问optional的subscript

var testScrores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brain"]?[0]

多层的optional chain, 原则是 * 如果试图查询的东西的类型并非是optional的,会由于使用了chain而变成optional * 如果正在查询的东西已经是optional的,它不会因为chain变得“更”optionaloptionaloptional)

下面是代码示例

if let johnsStreet = john.residence?.address?.street{
    println("John's street name is \(johnsStreet)")
}
// print nothing

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

if let johnsStreet = john.residence?.address?.street{
    println("John's street name is \(johnsStreet)")
}
// print "John's street name is Laurel Street"

if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginWithThe {
        println("John's building identifier begins with \"The\".")
    }
}