Swift 下标脚本

Swift 中的 下标(Subscripts) 是一种强大的语法糖,允许你使用 object[key] 的形式自定义访问逻辑。它是 Swift 类型(classstructenum)的一种特殊方法,常用于集合、字典、矩阵等需要“索引访问”的场景。


一、基本语法

subscript(参数列表) -> 返回类型 {
    get {
        // 返回一个值
    }
    set(新值) {
        // 将新值存储到对应位置
        // newValue 是默认参数名
    }
}

二、核心示例

1. 简单一维数组封装

struct NumberList {
    private var array: [Int] = []

    // 初始化
    init(count: Int, value: Int) {
        array = Array(repeating: value, count: count)
    }

    // 下标:读写
    subscript(index: Int) -> Int {
        get {
            guard index >= 0 && index < array.count else {
                fatalError("索引越界")
            }
            return array[index]
        }
        set {
            guard index >= 0 && index < array.count else {
                fatalError("索引越界")
            }
            array[index] = newValue
        }
    }
}

var list = NumberList(count: 5, value: 0)
list[0] = 10
list[2] = 30
print(list[2]) // 30

2. 二维矩阵(多参数下标)

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]

    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }

    // 计算一维索引
    private func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < 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
        }
    }
}

var matrix = Matrix(rows: 3, columns: 3)
matrix[0, 1] = 3.14
matrix[2, 2] = 9.9
print(matrix[0, 1]) // 3.14

3. 只读下标(省略 set

struct WeekDays {
    private let days = ["Sunday", "Monday", "Tuesday", "Wednesday",
                        "Thursday", "Friday", "Saturday"]

    subscript(index: Int) -> String {
        get {
            return days[index % days.count]  // 循环访问
        }
        // 无 set → 只读
    }
}

let week = WeekDays()
print(week[0])  // Sunday
print(week[7])  // Sunday(循环)
print(week[100]) // Tuesday

4. 带默认值的下标(安全访问)

struct SafeArray<T> {
    private var storage: [T]

    init(_ elements: T...) {
        self.storage = elements
    }

    subscript(safe index: Int) -> T? {
        return indices ~= index ? storage[index] : nil
    }
}

let numbers = SafeArray(1, 2, 3, 4, 5)
print(numbers[safe: 2])   // Optional(3)
print(numbers[safe: 10])  // nil

5. 字典式下标(键值访问)

struct DictionaryWrapper {
    private var dict: [String: String] = [:]

    subscript(key: String) -> String? {
        get { dict[key] }
        set { dict[key] = newValue }
    }

    // 默认值下标
    subscript(key: String, default defaultValue: String) -> String {
        get { dict[key] ?? defaultValue }
        set { dict[key] = newValue }
    }
}

var wrapper = DictionaryWrapper()
wrapper["name"] = "Alice"
print(wrapper["name"])                    // Optional("Alice")
print(wrapper["age", default: "0"])       // 0

6. 类型下标(Type Subscript)

enum HTTPStatus {
    case ok, notFound, serverError

    static subscript(code: Int) -> HTTPStatus? {
        switch code {
        case 200: return .ok
        case 404: return .notFound
        case 500...: return .serverError
        default: return nil
        }
    }
}

print(HTTPStatus[200]) // Optional(HTTPStatus.ok)
print(HTTPStatus[404]) // Optional(HTTPStatus.notFound)

三、下标重载(Overloading)

可以根据参数数量、类型、标签重载多个下标。

struct DataStore {
    private var intData: [Int] = []
    private var stringData: [String] = []

    // 按索引访问 Int
    subscript(index: Int) -> Int {
        return intData[index]
    }

    // 按索引访问 String
    subscript(index: Int) -> String {
        return stringData[index]
    }

    // 按键访问(泛型)
    subscript<T>(key: String) -> T? {
        fatalError("未实现")
    }
}

编译器根据调用上下文自动选择正确版本。


四、进阶用法

1. 下标 + inout

struct MutableBuffer {
    var buffer: [Int] = [1, 2, 3]

    subscript(index: Int) -> inout Int {
        get { &buffer[index] }
        // 无 set,inout 自动处理
    }
}

var buf = MutableBuffer()
buf[1] += 10  // 直接修改原始数据
print(buf.buffer) // [1, 12, 3]

2. 下标 + 泛型

struct GenericCollection<T> {
    private var items: [T] = []

    mutating func add(_ item: T) {
        items.append(item)
    }

    subscript(index: Int) -> T {
        return items[index]
    }
}

var strings = GenericCollection<String>()
strings.add("A")
strings.add("B")
print(strings[1]) // B

3. 下标 + 协议

protocol Subscriptable {
    subscript(index: Int) -> String { get }
}

struct MyData: Subscriptable {
    subscript(index: Int) -> String {
        return "Item \(index)"
    }
}

func printItem(_ obj: some Subscriptable, at index: Int) {
    print(obj[index])
}

printItem(MyData(), at: 5) // Item 5

五、最佳实践总结

场景推荐写法
数组/集合封装subscript(index: Int)
安全访问subscript(safe index: Int) -> T?
字典默认值subscript(key:default:)
矩阵/表格subscript(row:column:)
只读访问省略 set
类型索引static subscript(...)
边界检查assert / guard / fatalError

六、常见面试题

  1. 下标和方法有何不同?
    → 下标是语法糖,本质是 get/set 方法,但调用更简洁。
  2. 下标可以有多个参数吗?
    → 可以,数量和类型不限。
  3. 下标可以重载吗?
    → 可以,基于参数签名。
  4. 下标能抛出错误吗?
    → 可以,但需在调用处 try
   subscript(index: Int) throws -> Int { ... }
   try obj[0]
  1. 下标能异步吗?
    → 可以(Swift 5.5+):
   subscript(index: Int) async -> Data { ... }
   await obj[0]

七、小技巧

// 1. 链式下标(结合计算属性)
struct Grid {
    var data: [[Int]] = [[1,2],[3,4]]
    subscript(row: Int) -> [Int] {
        return data[row]
    }
}
let grid = Grid()
print(grid[0][1]) // 2

// 2. 动态成员下标(@dynamicMemberLookup)
@dynamicMemberLookup
struct JSON {
    subscript(dynamicMember key: String) -> Any? {
        return [:][key]
    }
}

// 3. 范围下标(自定义 Range)
struct Text {
    var content = "Hello, World!"
    subscript(r: Range<Int>) -> String {
        let start = content.index(content.startIndex, offsetBy: r.lowerBound)
        let end = content.index(content.startIndex, offsetBy: r.upperBound)
        return String(content[start..<end])
    }
}
let text = Text()
print(text[0..<5]) // Hello

八、高级话题(可继续提问)

  • 下标与 KeyPath 动态访问
  • 下标性能优化(Copy-on-Write)
  • SwiftUI 中的 @ViewBuilder 下标
  • 下标与 Collection 协议
  • 自定义 [] 运算符(Operator Overloading)

需要完整项目实战(如自定义 SafeDictionary)、性能对比、或与 Python 字典下标对比?欢迎继续提问!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部