Subscripts in Swift bieden een handige syntaxis voor het toegang krijgen tot elementen van een collectie, container of gebruikersgedefinieerd type met behulp van indexen, zoals bij arrays en woordenboeken. Ze kunnen worden gedeclareerd en overschreven in eigen klassen, structuren en enumeraties.
Subscripts zijn in Swift geïntroduceerd als een manier om genormaliseerde geadresseerde toegang tot objecten te bieden, te beginnen met collecties en zich daarna uit te breiden naar elke gebruikerstype. De belangrijkste taak is om de expressiviteit en leesbaarheid te verhogen, en toegangsmetodes voor gegevens te transformeren in een "natuurlijke" syntaxis die lijkt op de werking met een array.
In andere talen (bijv. Java) wordt de toegang tot een element van een structuur gedaan via getter/setter-methoden, wat de code omslachtig maakt. De drang naar betere leesbaarheid en het vergemakkelijken van gegevens toegang heeft subscripts voortgebracht. In Swift is er bijzondere aandacht vereist voor veilige toegang, getter en setter, mogelijke fouten en optionele teruggegeven waarden.
Een subscript kan worden gedeclareerd in elke klasse, structuur of enumeratie met behulp van het trefwoord subscript. Daarnaast worden meerdere overloads op parameters en teruggegeven types ondersteund.
Voorbeeld van een gebruikersgedefinieerde subscript:
struct Matrix { let rows: Int, columns: Int private var grid: [Double] 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 { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) return grid[(row * columns) + column] } set { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[1, 1] = 3.14 print(matrix[1, 1]) // 3.14
Belangrijke kenmerken:
Kan een subscript alleen met een getter worden gedeclareerd, zonder setter?
Ja. Als een subscript alleen voor leesdoeleinden nodig is, is het voldoende om alleen get te implementeren.
struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }
Is het toegestaan om meerdere subscripts met verschillende handtekeningen in één type te declareren?
Ja, subscripts kunnen worden overbelast (overload) op het aantal en type parameters.
struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }
Kan een subscript worden gedeclareerd in een protocol en geïmplementeerd in een structuur/klasse?
Ja, protocollen kunnen vereisen dat subscripts worden geïmplementeerd, en specifieke types zijn verplicht om ze te implementeren.
protocol MySubscriptable { subscript(index: Int) -> String { get set } } struct SubData: MySubscriptable { private var items = ["a", "b"] subscript(index: Int) -> String { get { return items[index] } set { items[index] = newValue } } }
Een ontwikkelaar implementeerde een subscript zonder grenzencontroles. Dit resulteerde in een crash van de applicatie bij het proberen toegang te krijgen tot een niet-bestaande index.
Voordelen: De code werd beknopter.
Nadelen: De applicatie ging onverwacht sluiten door plotselinge fouten.
Implementatie van een subscript met precondition en de mogelijkheid om optionele waarden terug te geven of fouten te gooien.
Voordelen: De code werd veiliger voor de gebruiker, fouten kunnen worden afgehandeld.
Nadelen: Extra foutafhandelingslogica moet worden ingevoerd en gedrag voor ongeldige indexen moet worden voorzien.