17.2. 为可选链式调用定义模型类

通过使用可选链式调用可以调用多层属性、方法和下标。这样可以在复杂的模型中向下访问各种子属性,并且判断能否访问子属性的属性、方法和下标。

下面这段代码定义了四个模型类,这些例子包括多层可选链式调用。为了方便说明,在 PersonResidence 的基础上增加了 Room 类和 Address 类,以及相关的属性、方法以及下标。

Person 类的定义基本保持不变:

class Person {
    var residence: Residence?
}

Residence 类比之前复杂些,增加了一个名为 rooms 的变量属性,该属性被初始化为 [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 数组的快捷方式,即提供可读写的下标来访问 rooms 数组中指定位置的元素。

此外,Residence 还提供了 printNumberOfRooms 方法,这个方法的作用是打印 numberOfRooms 的值。

最后,Residence 还定义了一个可选属性 address,其类型为 Address?Address 类的定义在下面会说明。

Room 类是一个简单类,其实例被存储在 rooms 数组中。该类只包含一个属性 name,以及一个用于将该属性设置为适当的房间名的初始化函数:

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

最后一个类是 Address,这个类有三个 String? 类型的可选属性。buildingName 以及 buildingNumber 属性分别表示大厦的名称和号码,第三个属性 street 表示大厦所在街道的名称:

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

Address 类提供了 buildingIdentifier() 方法,返回值为 String?。 如果 buildingName 有值则返回 buildingName。或者,如果 buildingNumberstreet 均有值,则返回两者拼接得到的字符串。否则,返回 nil