Avec la sortie de Swift 5.5, le langage a intégré le concept de modèle de propriété et de sémantiques de déplacement, qui renforcent le contrôle sur la possession des données et permettent au compilateur d'optimiser les déplacements, réduisant ainsi le nombre de copies, ce qui est pertinent pour des scénarios à haute performance.
Les sémantiques de déplacement impliquent que lors de la transmission d'une valeur (par exemple, struct), on peut "transmettre" la possession de cette valeur sans copie. Le compilateur peut alors invalider la variable d'origine (analogue au move en C++). Actuellement, le modèle de propriété et les sémantiques de déplacement sont davantage réalisés comme une expérience (isolation des acteurs, types envoyables, @_move, consumable/self-consumable) et promettent d'apparaître dans l'API publique.
La principale différence avec l'ARC est que les sémantiques de déplacement s'appliquent aux value types, tandis que l'ARC gère la durée de vie des objets de référence.
Exemple (sémantique de possession, Swift 5.5+):
func consume<T>(_ x: __owned T) { /* ... */ } struct LargeArray { var storage: [Int] mutating func clear() { storage.removeAll() } consuming func consumeSelf() { // self n'est plus accessible après l'appel } }
La gestion de la possession permet d'éviter des copies inattendues lors du travail avec de grandes structures.
Nuances :
Quelle est la différence entre passer un objet structure à une fonction par valeur, par référence et par sémantique de déplacement dans Swift ?
Réponse :
Exemple:
func foo(_ x: MyStruct) { /* copie */ } func bar(_ x: inout MyStruct) { /* par référence */ } func baz(_ x: __owned MyStruct) { /* sémantiques de déplacement, ne copie pas */ }
Histoire
Dans le projet, lors de la transmission de grandes structures via des fonctions, il se produisait toujours des copies implicites, augmentant les coûts de mémoire. Après l'implémentation des sémantiques de déplacement expérimentales et d'une gestion de la possession plus réfléchie, il a été possible de redistribuer la charge de manière propre et d'accélérer les sections critiques.
Histoire
De nombreux développeurs utilisaient incorrectement inout, pensant qu'il réalisait un move, tandis que la valeur restait accessible, entraînant une possession ambiguë de la variable, ce qui conduisait à des bogues et à une rupture de la logique.
Histoire
Erreur de gestion des données entre les threads : absence du qualifier Sendable sur les structures, ce qui entraînait une copie inattendue ou des erreurs de possession lors du travail asynchrone avec de grandes structures via un acteur ou une tâche.