在Swift中,下标提供了一种方便的语法,通过索引访问集合、容器或用户自定义类型的元素,就像数组和字典一样。它们可以在自己的类、结构体和枚举中声明和重载。
下标在Swift中诞生是为了统一对对象的索引访问,最初用于集合,随后扩展到任何用户自定义类型。其关键任务是提高表达力和可读性,将数据访问的方法转变为类似数组操作的"自然"语法。
在其他语言中(例如Java),对结构元素的访问通过get/set方法实现,导致代码冗长。对更好可读性和简化数据访问的追求催生了下标。在Swift中,需要特别关注安全访问、getter和setter、可能的错误以及返回的可选值。
可以通过关键字subscript在任何类、结构体或枚举中声明下标。另外,支持多参数和返回类型的重载。
自定义下标示例:
struct Matrix { let rows: Int, columns: Int private var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns grid = Array(repeating: 0.0, count: rows * columns) } subscript(row: Int, column: Int) -> Double { get { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) return grid[(row * columns) + column] } set { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[1, 1] = 3.14 print(matrix[1, 1]) // 3.14
关键特性:
可以仅声明只具有getter的下标,且没有setter吗?
可以。如果下标仅用于读取,只需实现getter即可。
struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }
是否允许在同一类型中声明多个具有不同签名的下标?
可以,下标可以根据参数的数量和类型进行重载。
struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }
下标可以在协议中声明,并在结构体/类中实现吗?
可以,协议可以要求实现下标,而具体类型必须实现它们。
protocol MySubscriptable { subscript(index: Int) -> String { get set } } struct SubData: MySubscriptable { private var items = ["a", "b"] subscript(index: Int) -> String { get { return items[index] } set { items[index] = newValue } } }
开发者实现了没有边界检查的下标。结果,在访问不存在的索引时,应用程序因运行时错误崩溃。
优点: 代码更加简洁。
缺点: 应用程序因意外错误而崩溃。
实现具有预条件和返回可选值或抛出错误的下标。
优点: 代码对用户更安全,错误可以被处理。
缺点: 需要设计额外的错误处理逻辑,并考虑无效索引的行为。