【Swift 3.1】17 - 可选链 (Optional Chaining)

【Swift 3.1】17 - 可选链 (Optional Chaining)

自从苹果2014年发布Swift,到现在已经两年多了,而Swift也来到了3.1版本。去年利用工作之余,共花了两个多月的时间把官方的Swift编程指南看完。现在整理一下笔记,回顾一下以前的知识。有需要的同学可以去看官方文档>>。


可选链是在一个可能为nil的可选类型上查询和调用属性、方法和下标的一个过程。如果这个可选类型有值,那么属性、方法或者下标调用成功;如果可选类型值为nil,调用属性、方法或者下标就会返回nil。多个查询可以连在一起,只要任何一个环节为nil,那么整个链条失败。

可选链作为强制解包的一个方法 (Optional Chaining as an Alternative to Forced Unwrapping)

我们想访问可选类型的属性、方法或者下标,在这个可选类型的后面加上?来表示可选链。可选链返回的结果永远是可选类型的值,即使属性、方法或者下标不是可选类型。

首先,创建两个类PersonResidence:

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

创建一个Person实例,residence属性默认为nil

let john = Person()

如果要访问Person实例的residencenumberOfRooms属性,需要在residence后面用!解包,这将会触发运行时错误,因为residence没有值:

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

下面使用可选链来访问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实例赋给john.residence,那么john.residence就不为nil了,接着在使用可选链解包,解包成功:

john.residence = Residence()

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

为可选链定义更多Class模型 (Defining Model Classes for Optional Chaining)

我们可以使用多层次的可选链调用属性、方法和下标。下面将用四个类来演示:

第一个是Person,和上面的一样:

class Person {
    var residence: 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?
}

第三个是Room

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

最后一个是Address

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

使用可选链访问属性 (Accessing Properties Through Optional Chaining)

创建一个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.residence的值还是为nil。把someAddress赋给address也是可选链的一部分,但是=右边的代码其实没有运行。直接通过上面的代码不容易看出,我们可以定义一个方法来创建someAddress来测试下:

func createAddress() -> Address {
    print("Function was called.")
    
    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"
    
    return someAddress
}
john.residence?.address = createAddress()

我们可以发现,createAddress()并没有调用,因为Function was called.没有打印。

使用可选链调用方法 (Calling Methods Through Optional Chaining)

我们可以使用可选链来调用方法,并检查那个方法是否调用成功,即使那个方法没有返回值。注意:没有返回值的方法,有一个默认返回类型Void

使用可选链调用没有返回值的方法,方法的返回值将会是Void?,而不是Void,因为使用可选链访问的结果都是可选类型。

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."

也可以使用类似上面这种检查方法来检查使用可选链赋值是否成功。使用可选链赋值会返回一个Void?

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."

使用可选链访问下标 (Accessing Subscripts Through Optional Chaining)

注意:使用可选链访问下标,要把?放在中括号前面,而不是后面。

例如:

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.residencenil,所以调用下标失败。

如果我们创建Residence实例,并给rooms数组赋值,然后再把Residence实例赋给john.residence

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.")
}
// Prints "The first room name is Living Room."
访问可选类型的下标 (Accessing Subscripts of Optioinal Type)

如果一个下标返回一个可选类型的值,例如Swift的Dictionary类型的键下标:

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

连接多层次链 (Linking Multiple Levels of Chaining)

如果想要获取的类型不是可选类型,因为可选链,他将会变成可选类型;如果想要获取的类型是可选类型,因为可选链,那么它依然是可选类型。例如,如果想要获取的值是Init类型,那么将会返回Init?类型,不管可选链有多少层;如果想要获取的值是Init?类型,那么仍然返回Init?类型,不管可选链有多少层。

例如:

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现在是有值的,但是john.residence.addressnil,所以john.residence?.address?.streetnil

如果设置一个真实的Address实例给john.residence.address,并且设置一个真实的值给street属性,我们可以使用多层次可选链访问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.")
}
// Prints "John's street name is Laurel Street."

链接有可选类型返回值的方法 (Chaining on Methods with Optional Return Values)

下面是使用可选链访问有可选类型返回值的方法:

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("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 {
        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"."

第十七部分完。下个部分:【Swift 3.1】18 - 错误处理 (Error Handling)


如果有错误的地方,欢迎指正!谢谢!

你可能感兴趣的:(【Swift 3.1】17 - 可选链 (Optional Chaining))