Swift 语法:可选链接(Optional Chaining)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/43370557

1、引言

可选链接是一个可以请求和调用可选目标(该目标可能为 nil)的属性、方法或者下标的过程。

2、为什么会有可选链接?

我们先来看一个示例:
class Residence {
    var numberOfRooms = 1
}
class Person {
    var residence: Residence?
}

let john = Person()
// let roomCount = john.residence!.numberOfRooms
// 由于 john.residence 为 nil,通过声明符(!)强制获得 numberOfRooms 属性会编译出错

let residence = Residence()
let jack = Person()
jack.residence = residence
let roomCount = jack.residence!.numberOfRooms 
// 只有当 jack.residence 不是 nil 时才能运行通过
为了解决上述 jack.residence 可能为空而又必须获取 numberOfRooms 值的场景,可选链接提供了一种方法:通过(?)替代上述的(!)。
let john = Person() 
let roomCount = john.residence?.numberOfRooms 
// 虽然此时 jack.residence 为 nil,但是运行通过
注意:通过调用可选链接的返回结果与原本的返回结果具有相同的类型,但是将原本的返回结果包装成了一个可选的。
如:可选链接调用成功时原本应该返回(someType)的属性将会返回(someType?)。
叹号(!)和问号(?)的区别:
叹号(!):强制解析符号。如果解析对象为 nil 会编译出错。
问号(?):可选链接符号。就算解析对象为 nil 也可运行,但是返回结果为 nil。

3、可选链接的运用

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
        }
    }
}
class Residence {
    var rooms = [Room]()
    var address: Address?
    var numberOfRooms: Int {
        return rooms.count
    }
    func printNumberOfRooms() {
        println("The number of rooms is \(numberOfRooms)")
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
}
class Person {
    var residence: Residence?
}

// 1、通过可选链接调用属性和设置属性
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    println("John's residence has \(roomCount) room(s).")
} else {
    println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
// 运行通过,但是由于 residence 为 nil,所以设置失败

// 2、通过可选链接调用方法
if john.residence?.printNumberOfRooms() != nil {
    println("It was possible to print the number of rooms.")
} else {
    println("It was not possible to print the number of rooms.")
}
// prints "It was not possible to print the number of rooms."
if (john.residence?.address = someAddress) != nil {
    println("It was possible to set the address.")
} else {
    println("It was not possible to set the address.")
}
// prints "It was not possible to set the address."

// 3、通过可选链接调用下标和设置值
if let firstRoomName = john.residence?[0].name {
    println("The first room name is \(firstRoomName).")
} else {
    println("Unable to retrieve the first room name.")
}
// prints "Unable to retrieve the first room name."
john.residence?[0] = Room(name: "Bathroom")
// 运行通过,设置失败

// 补充:可选类型调用下标
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72

// 4、多层可选链接
if let johnsStreet = john.residence?.address?.street {
    println("John's street name is \(johnsStreet).")
} else {
    println("Unable to retrieve the address.")
}
// prints "Unable to retrieve the address."

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
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 {
    println("John's street name is \(johnsStreet).")
} else {
    println("Unable to retrieve the address.")
}
// prints "John's street name is Laurel Street."

// 5、可选链接返回方法的值
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    println("John's building identifier is \(buildingIdentifier).")
}
// prints "John's building identifier is The Larches."

if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        println("John's building identifier begins with \"The\".")
    } else {
        println("John's building identifier does not begin with \"The\".")
    }
}
// prints "John's building identifier begins with "The"."
仔细观察上述示例,可能有人会对调用下标 john.residence?[0].name 和返回方法的值 john.residence?.address?.buildingIdentifier()?.hasPrefix("The") 中(?)的位置有疑惑:
A、调用下标中的解释:因为可选链接的问号一般直接跟在可选表达语句的后面。
B、返回方法的值中的解释:你想要的是 buildingIdentifier 方法的返回值,而不是方法本身。

4、结语

文章最后更新时间:2015年2月3日09:37:48。
欲了解更细致的请参考官方文档:Optional Chaining

你可能感兴趣的:(swift,chaining,Optional,可选链接,Playgrounds)