Introduction : le débat éternel entre static et class en Swift
\nSi vous avez déjà programmé en Swift, vous avez sûrement été confronté à cette situation : vous devez créer une méthode ou une variable qui appartient au type lui-même, et non à son instance. Et là se pose la question : faut-il utiliser le mot-clé static ou class ? Sur Stack Overflow, cette question a rassemblé des milliers de vues, et ce n'est pas pour rien : la différence est subtile, mais cruciale pour une bonne conception de l'architecture de l'application.
Dans ce tutoriel, nous allons enfin comprendre en quoi static et class diffèrent en Swift, quand les utiliser, et comment éviter les pièges de l'héritage. C'est parti !
Que sont les propriétés et méthodes de type ?
\nAvant de plonger dans les détails, rappelons les bases. En Swift, chaque classe, structure ou énumération possède deux types de membres :
\n- \n
- Les membres d'instance (instance) — ils appartiennent à un objet spécifique. Pour les utiliser, il faut créer une instance :
let obj = MyClass(). \n - Les membres de type (type) — ils appartiennent au type lui-même. On les appelle via le nom du type :
MyClass.someMethod(). \n
Pour créer des propriétés et méthodes de type en Swift, on utilise les mots-clés static et class. Et c'est là que les choses deviennent intéressantes.
Static : strict et immuable
\nLe mot-clé static est votre ami fiable lorsque vous souhaitez créer une propriété ou une méthode qui ne peut pas être redéfinie dans une sous-classe. Il fonctionne de la même manière pour les classes, les structures et les énumérations.
Regardons un exemple. Imaginons que nous développons une application de gestion d'étudiants et que nous voulons stocker le nombre total d'objets créés :
\n\nclass Student {\n static var totalCount = 0\n \n let name: String\n \n init(name: String) {\n self.name = name\n Student.totalCount += 1\n }\n \n static func printTotal() {\n print("Nombre total d'étudiants : \\(totalCount)")\n }\n}\n\nlet s1 = Student(name: "Ivan")\nlet s2 = Student(name: "Maria")\nStudent.printTotal() // Affiche : Nombre total d'étudiants : 2\n\nRemarquez que nous accédons à totalCount et printTotal() via le nom de la classe Student, et non via une instance. C'est pratique pour les compteurs, les configurations par défaut ou la mise en cache de données.
Static dans les structures et énumérations
\nDans les structures et les énumérations, static est le seul moyen de déclarer des membres de type. Par exemple :
enum HTTPMethod {\n case get\n case post\n case put\n \n static let defaultMethod: HTTPMethod = .get\n}\n\nstruct AppConfig {\n static let apiURL = "https://api.example.com"\n static let timeout: Double = 30.0\n}\n\nIci, static fonctionne parfaitement car les structures et les énumérations ne supportent pas l'héritage, donc la redéfinition n'est pas nécessaire.
Class : la flexibilité de l'héritage
\nLe mot-clé class, quant à lui, est un outil pour ceux qui travaillent avec des classes (et uniquement elles) et qui souhaitent permettre aux sous-classes de redéfinir des méthodes ou des propriétés. class ne peut pas être utilisé dans les structures ou les énumérations.
Supposons que nous ayons une classe de base Vehicle et que nous voulions que chaque sous-classe puisse fournir sa propre version de la description :
class Vehicle {\n class func description() -> String {\n return "C'est un véhicule"\n }\n}\n\nclass Car: Vehicle {\n override class func description() -> String {\n return "C'est une voiture"\n }\n}\n\nclass Bicycle: Vehicle {\n override class func description() -> String {\n return "C'est un vélo"\n }\n}\n\nprint(Vehicle.description()) // C'est un véhicule\nprint(Car.description()) // C'est une voiture\nprint(Bicycle.description()) // C'est un vélo