Mechanizm subscript w Swift pojawił się wraz z językiem w 2014 roku i jest przeznaczony do uzyskiwania dostępu do danych kolekcji w bardziej zrozumiały sposób. Subskrypty pozwoliły na dostęp do elementów tablic, słowników i innych kolekcji za pomocą nawiasów kwadratowych. Nowoczesny Swift umożliwia tworzenie użytkowych subskryptów dla własnych typów, co czyni API intuicyjnie zrozumiałym jak w standardowych strukturach danych.
Problem: Dawniej metody dostępu były bardziej skomplikowane (na przykład przez .getElement(at:)), a subscript sprawia, że interakcja z typem jest bardziej zwięzła. Niewłaściwa implementacja może jednak prowadzić do nieoptymalnego kodu lub błędów dostępu poza granice kolekcji.
Rozwiązanie: Do realizacji subscript używa się słowa kluczowego subscript, po którym podaje się parametry i typ zwracany. Subscript może być tylko do odczytu lub do odczytu i zapisu. Ponadto, subskrypty mogą być przeciążane dla różnych parametrów.
Przykład kodu:
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, "Indeks poza zakresem") return grid[(row * columns) + column] } set { assert(row >= 0 && row < rows && column >= 0 && column < columns, "Indeks poza zakresem") grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[0, 1] = 3.14 print(matrix[0, 1]) // 3.14
Kluczowe cechy:
Czy można przypisać subscript tylko do odczytu? Czy subscript zawsze musi mieć get i set?
Subscript może być tylko do odczytu (tylko get), jeśli w implementacji brakuje set. Nie jest konieczne implementowanie obu akcesorów, co jest podobne do właściwości.
Przykład kodu:
subscript(index: Int) -> Int { get { return index * 2 } }
Czy można przekazywać zmienione parametry przez inout w subscript?
Nie, subscript nie obsługuje parametrów inout w sygnaturze. Wszystkie parametry subskryptów są traktowane jako stałe let w ciele akcesorów.
Czy subskrypty mogą zwracać typ opcjonalny?
Tak, subskrypty mogą zwracać wartości opcjonalne, co jest wygodne, na przykład, do bezpiecznego dostępu do elementów kolekcji bez ryzyka wyjścia poza tablicę.
Przykład kodu:
extension Array { subscript(safe index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } }
Implementacja subscript bez walidacji na wyjście poza granice dla złożonego typu macierzy, co prowadzi do awarii aplikacji przy błędach indeksu.
Zalety:
Wady:
Dodana opcjonalna wersja subscript do bezpiecznej indeksacji i obsługi błędów, co czyni API oczywistym i zabezpieczonym.
Zalety:
Wady: