Skip to content

下标基础

下标可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。下标语法类似于计算属性,使用 subscript 关键字定义。

swift
struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {  // 只读下标
        return multiplier * index
    }
}

let threeTimesTable = TimesTable(multiplier: 3)
print(threeTimesTable[6])  // 输出 18

读写下标

下标可以同时包含 getter 和 setter:

swift
struct Matrix {
    var grid: [Double]
    let rows: Int, columns: Int
    
    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 {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
    
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5  // 调用 setter
print(matrix[1, 0])  // 调用 getter

下标特性

  1. 多参数下标

    swift
    subscript(row: Int, column: Int) -> Double { ... }
  2. 类型下标(静态下标):

    swift
    enum Planet: Int {
        case mercury = 1, venus, earth, mars
        
        static subscript(n: Int) -> Planet {
            return Planet(rawValue: n)!
        }
    }
    
    let mars = Planet[4]  // 调用类型下标
  3. 下标重载

    swift
    struct Path {
        private var points: [CGPoint]
        
        // 通过索引访问
        subscript(index: Int) -> CGPoint {
            return points[index]
        }
        
        // 通过名称访问
        subscript(name: String) -> CGPoint? {
            return points.first { $0.name == name }
        }
    }

实际应用

  1. 字典访问

    swift
    var legs = ["ant": 6, "snake": 0]
    legs["human"] = 2  // 调用字典的下标setter
    print(legs["ant"]!)  // 调用getter
  2. 安全数组访问

    swift
    struct SafeArray {
        private var array: [Int]
        
        subscript(index: Int, default defaultValue: Int = 0) -> Int {
            return indices.contains(index) ? array[index] : defaultValue
        }
    }
  3. 多维数据结构

    swift
    struct Cube {
        // 三维下标访问
        subscript(x: Int, y: Int, z: Int) -> Int { ... }
    }

关键注意事项

  1. 字典下标返回可选值

    swift
    let value = legs["unicorn"]  // Int?
  2. 断言检查

    swift
    assert(indexIsValid(row: row, column: column), "Index out of range")
  3. 性能考虑

    • 避免在下标中进行复杂计算
    • 考虑使用缓存优化重复访问
  4. 与函数区别

    • 下标不能使用 inout 参数
    • 不支持默认参数值(但可以通过重载模拟)

高级用法

  1. 动态成员查找(Swift 4.2+):

    swift
    @dynamicMemberLookup
    struct DynamicStruct {
        subscript(dynamicMember member: String) -> String {
            return "Accessed \(member)"
        }
    }
    
    let s = DynamicStruct()
    print(s.hello)  // 输出 "Accessed hello"
  2. 类型约束下标

    swift
    struct GenericContainer<T> {
        subscript<U: Numeric>(index: U) -> T { ... }
    }

通过合理使用下标,可以大大提升代码的可读性和简洁性,特别是在处理集合类数据结构时。下标的设计应当保持直观,符合使用者的预期。