Historia pytania:
Subscript to mechanizm uzyskiwania dostępu do wartości w kolekcjach, strukturach i innych typach za pomocą nawiasów kwadratowych. Został dodany do Swift na wzór innych języków (np. indeksowanie w tablicach Pythona, operator [] w C++/Java), aby uczynić pracę z kolekcjami i podobnymi obiektami bardziej intuicyjną.
Problem:
Nie wszystkie typy domyślnie wspierają uzyskiwanie dostępu według indeksu jak tablica. Kiedy mamy strukturę lub klasę z danymi, dostęp do elementów często logiczniej zrealizować przez zwykłą składnię (jak myObject[index]), a nie metodą.
Rozwiązanie:
Swift pozwala na samodzielną implementację subscript dla dowolnego użytkownika typu. Można to zrobić zarówno dla odczytu, jak i zapisu. Przykład — struktura implementująca subscripting do pracy z macierzą kwadratową:
struct Matrix { let size: Int private var grid: [Int] init(size: Int) { self.size = size self.grid = Array(repeating: 0, count: size * size) } subscript(row: Int, column: Int) -> Int { get { precondition(indexIsValid(row, column), "Index out of range") return grid[(row * size) + column] } set { precondition(indexIsValid(row, column), "Index out of range") grid[(row * size) + column] = newValue } } private func indexIsValid(_ row: Int, _ column: Int) -> Bool { return row >= 0 && row < size && column >= 0 && column < size } } var m = Matrix(size: 3) m[1,2] = 7 print(m[1,2]) // 7
Kluczowe cechy:
Czy subscript może być używany tylko dla kolekcji?
Nie, subscript może być implementowany dla dowolnych typów: struktura, klasa, enum. Typ danych wewnątrz nie musi być koniecznie kolekcją, ważna jest logika przetwarzania zapytań przez subscript.
Czy można zrobić kilka subscript o różnej sygnaturze w jednym typie?
Tak, dopuszczalne jest przeciążanie subscriptingu o różnej sygnaturze parametrów:
struct Example { subscript(index: Int) -> Int { return index } subscript(key: String) -> String { return key.uppercased() } }
Czy można używać subscript jako właściwości obliczanej bez set?
Tak, subscript może być tylko do odczytu (get-only), w takim przypadku próba zapisu spowoduje błąd kompilacji.
struct ReadOnly { subscript(index: Int) -> Int { index * index } }
W dużej strukturze zaimplementowano subscript bez sprawdzania zakresu, co prowadzi do awarii aplikacji przy niepoprawnym indeksie.
Zalety:
Wady:
Matrix zaimplementowano z precondition na zakres indeksów. Wszelkie błędy są natychmiast wychwytywane, nie trafiają do produkcji.
Zalety:
Wady: