ProgrammationDéveloppeur iOS

Comment fonctionne l'allocation sur la pile et l'allocation sur le tas en Swift ? Dans quels cas les objets sont-ils placés sur la pile ou sur le tas, comment cela affecte-t-il la performance et le cycle de vie des objets ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Swift, les types de valeur (value types), tels que struct, enum et tuple, sont généralement placés sur la pile, tandis que les types de référence (reference types), tels que class, sont placés dans le tas (heap).

L'allocation sur la pile est rapide, automatique et utilisée pour stocker des variables locales ayant une durée de vie courte. L'allocation sur le tas nécessite des coûts supplémentaires et est utilisée pour des objets ayant une durée de vie ou une taille indéfinies.

Exemple:

struct Point { var x: Int; var y: Int } let p1 = Point(x: 2, y: 3) // Sur la pile class Node { var value: Int; init(value: Int) { self.value = value } } let n1 = Node(value: 5) // Dans le tas

Nuances :

  • Le compilateur/optimiseur Swift peut même placer des struct dans le tas si elles sont imbriquées dans une class ou placées dans une collection.
  • Les value types sont copiés lors de la transmission, ce qui entraîne généralement une copie sur la pile, mais Swift utilise copy-on-write pour les collections.
  • L'allocation dans le tas nécessite un contrôle sur la durée de vie des objets (ARC). Les objets sur la pile sont automatiquement détruits lors de leur sortie de portée.

Performance : L'allocation sur la pile est plus rapide, car elle ne nécessite pas de gestion de la mémoire dans le tas et de la gestion de la durée de vie des objets.

Question piège

Tous les structures (struct) sont-elles toujours uniquement placées sur la pile ?

Réponse :
Non ! Bien que les structures soient des types de valeur et qu'elles soient souvent placées sur la pile, le compilateur peut les placer dans le tas si elles se trouvent à l'intérieur d'un objet de classe, d'un tableau, d'un dictionnaire ou sont utilisées comme valeur capturée dans un closure. Par exemple :

class Box { var point: Point init(point: Point) { self.point = point } } let box = Box(point: Point(x: 1, y: 2))

Ici, l'instance Point sera stockée dans le tas, car elle appartient à la classe Box.

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet


Histoire

Un développeur a essayé d'optimiser les performances des structures, pensant que des structures volumineuses seraient toujours dans la pile, sans tenir compte du fait que les collections (Array, Dictionary) utilisent l'allocation sur le tas, ce qui a conduit à une augmentation inattendue de l'utilisation de la mémoire dans le projet.


Histoire

Dans le projet, il n'a pas été pris en compte que le closure capture la valeur de la structure, qui se retrouve dans le tas, influençant la durée de vie de l'objet. Cela a augmenté la durée de vie des variables et a conduit à des fuites de mémoire, car on s'attendait à un relâchement automatique lors de la sortie de portée.


Histoire

L'utilisation de tableaux de structures lourdes sans comprendre copy-on-write a conduit à des opérations de copie coûteuses en raison du transfert de collections entre les threads, diminuant les performances et provoquant des latences dans l'interface utilisateur.