编程iOS 开发者

解释一下 Swift 中集合的下标机制是如何工作的,以及如何将其应用于自定义类型?举例说明。

用 Hintsage AI 助手通过面试

答案。

Swift 中的下标机制自 2014 年语言问世以来就已引入,旨在以更便捷的语法访问集合的数据。下标允许通过方括号访问数组、字典和其他集合的元素。现代 Swift 允许为自定义类型创建用户自定义下标,使 API 直观易懂,就像标准数据结构一样。

问题: 以前的访问方法看起来更加繁琐(例如,通过 .getElement(at: )),而下标使与类型的交互更为简洁。然而,错误的实现可能导致低效的代码或访问集合边界之外的错误。

解决方案: 要实现下标,使用关键字 subscript,然后指定参数和返回值。下标可以是只读的,也可以是读写的。此外,下标可以为不同的参数进行重载。

代码示例:

struct Matrix { let rows: Int let columns: Int var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns self.grid = Array(repeating: 0.0, count: rows * columns) } subscript(row: Int, column: Int) -> Double { get { assert(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range") return grid[(row * columns) + column] } set { assert(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range") grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[0, 1] = 3.14 print(matrix[0, 1]) // 3.14

关键特性:

  • 下标支持通过熟悉的语法 [index] 读取和写入元素。
  • 可以接受多个参数并且可以重载。
  • 通过下标可以使类型成为集合。

陷阱问题。

下标可以只读吗?还是下标必须始终有 get 和 set?

如果在实现中没有 set,则下标可以是只读的(仅 get)。不必实现两个访问器,这与属性类似。

代码示例:

subscript(index: Int) -> Int { get { return index * 2 } }

可以通过 inout 在下标中传递可变参数吗?

不可以,下标不支持在签名中使用 inout 参数。所有下标的参数均在访问器体内作为 let 常量接收。

下标可以返回可选类型吗?

可以,下标可以返回可选值,这在安全访问集合元素时非常方便,无需担心超出数组范围。

代码示例:

extension Array { subscript(safe index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } }

常见错误和反模式

  • 在实现下标时跳过范围检查。
  • 访问器中的逻辑过于复杂,违反单一职责原则。
  • 下标与显式方法 get/set 之间的语义差异不明显,导致使用时混淆。

实际案例

负面案例

在复合类型矩阵的实现中,没有对下标进行边界验证,导致索引错误时应用崩溃。

优点:

  • 实现简单快速,代码简洁。

缺点:

  • 由于缺少 assert 或 guard,可能出现运行时错误。
  • 难以测试,代码扩展性差。

正面案例

添加了安全索引的可选版本的下标,处理错误,使 API 明确且安全。

优点:

  • 更安全的方法,提前警告可能的问题。
  • 可预测的行为,减少运行时崩溃。

缺点:

  • 客户端需要处理可选值。