编程中级/高级iOS开发者

在Swift中,什么是下标(subscripts),并且如何重载或扩展它们?请给出高级用法示例。

用 Hintsage AI 助手通过面试

回答。

在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

关键特性:

  • 可以定义多个不同类型和参数数量的下标
  • 下标可以是只读(get)或读写(get/set)
  • 可以使用不同类型的索引进行下标操作,甚至可以使用范围和字符串

诡计性问题。

可以仅声明只具有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 } } }

常见错误和反模式

  • 在没有边界检查的情况下进行不安全的访问(预条件检查)
  • 下标参数数据类型错误
  • 不必要的过度使用下标,反而用方法会更好

现实生活中的示例

负面案例

开发者实现了没有边界检查的下标。结果,在访问不存在的索引时,应用程序因运行时错误崩溃。

优点: 代码更加简洁。

缺点: 应用程序因意外错误而崩溃。

正面案例

实现具有预条件和返回可选值或抛出错误的下标。

优点: 代码对用户更安全,错误可以被处理。

缺点: 需要设计额外的错误处理逻辑,并考虑无效索引的行为。