ProgrammationDéveloppeur iOS Mid/Senior

Qu'est-ce que les subscripts en Swift et comment peuvent-ils être redéfinis ou étendus ? Donnez un exemple d'utilisation avancée.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Les subscripts en Swift offrent une syntaxe pratique pour accéder aux éléments d'une collection, d'un conteneur ou d'un type utilisateur à l'aide d'indices, comme dans les tableaux et les dictionnaires. Ils peuvent être déclarés et redéfinis dans vos propres classes, structures et énumérations.

Historique de la question

Les subscripts sont apparus en Swift comme un moyen de normaliser l'accès indexé aux objets, en commençant par les collections, puis en s'étendant à tous les types utilisateurs. L'objectif clé est d'améliorer l'expressivité et la lisibilité, transformant les méthodes d'accès aux données en une syntaxe "naturelle" qui ressemble au travail avec un tableau.

Problème

Dans d'autres langages (par exemple, Java), l'accès à un élément d'une structure se fait par des méthodes get/set, ce qui rend le code alambiqué. Le désir d'une meilleure lisibilité et d'un accès facilité aux données a engendré les subscripts. En Swift, une attention particulière est requise pour un accès sûr, un getter et un setter, les erreurs potentielles et les valeurs de retour optionnelles.

Solution

Un subscript peut être déclaré dans n'importe quelle classe, structure ou énumération à l'aide du mot-clé subscript. De plus, plusieurs surcharge par paramètres et types de retour sont prises en charge.

Exemple de subscript utilisateur :

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

Caractéristiques clés :

  • Plusieurs subscripts peuvent être définis avec différents types et nombres de paramètres.
  • Un subscript peut être seulement get, ou get/set.
  • On peut utiliser un subscript avec différents types d'indices, même des intervalles et des chaînes.

Questions pièges.

Peut-on déclarer un subscript seulement avec un getter, sans setter ?

Oui. Si le subscript est uniquement nécessaire pour la lecture, il suffit de mettre en œuvre uniquement get.

struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }

Est-il permis de déclarer plusieurs subscripts avec différentes signatures dans un même type ?

Oui, les subscripts peuvent être surchargés (overload) par le nombre et le type de paramètres.

struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }

Un subscript peut-il être déclaré dans un protocole et implémenté dans une structure/classe ?

Oui, les protocoles peuvent exiger l'implémentation de subscripts, et les types spécifiques doivent les implémenter.

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 } } }

Erreurs typiques et anti-patterns

  • Accès non sécurisé sans vérification des limites (precondition, vérification des limites)
  • Erreurs de type de données pour les paramètres de subscript.
  • Utilisation excessive de subscripts sans nécessité, où il est préférable d'utiliser des méthodes.

Exemple pratique

Cas négatif

Un développeur a implémenté un subscript sans vérification des limites. En conséquence, lorsque l'on accède à un indice inexistant, l'application plante avec une erreur à l'exécution.

Avantages : Le code est devenu plus concis.

Inconvénients : L'application a commencé à planter en raison d'erreurs imprévues.

Cas positif

Mise en œuvre d'un subscript avec des preconditions et la possibilité de retourner optionnels ou de lever des erreurs.

Avantages : Le code est devenu plus sûr pour l'utilisateur, les erreurs peuvent être gérées.

Inconvénients : Besoin d'intégrer une logique supplémentaire pour la gestion des erreurs et de prévoir le comportement pour des indices non valides.