Les structures et les classes sont des constructions polyvalentes et flexibles. Vous définissez des propriétés (variables ou constantes) et des méthodes (fonctions) pour ajouter des fonctionnalités à vos structures et classes en utilisant la même syntaxe que vous utilisez pour définir des constantes, des variables et des fonctions.

Dans Swift, vous définissez une structure ou une classe dans un seul fichier, et l'interface externe de cette classe ou structure est automatiquement mise à disposition pour d'autres codes à utiliser.

Une instance d'une classe est également connue sous le nom d'objet.

Comparaison des structures et classes

Les structures et les classes Swift ont de nombreux points communs :

Les classes ont des capacités supplémentaires :

  • L'héritage permet à une classe d'hériter des caractéristiques d'une autre.
  • Le type casting vous permet de vérifier et d'interpréter le type d'une instance de classe au moment de l'exécution
  • Les déinitialiseurs permettent à une instance d'une classe de libérer toutes les ressources qu'elle a affecté
  • Le comptage de références autorise plusieurs références à une instance de classe

Les capacités supplémentaires prises en charge par les classes se font au prix d'une complexité accrue. En règle générale, préférez les structures car elles sont plus faciles à raisonner et utilisez les classes lorsqu'elles sont appropriées ou nécessaires.

En pratique, cela signifie que la plupart des types de données personnalisées que vous définissez seront des structures et des énumérations.

Syntaxe des structures et classes

Les structures et les classes ont une syntaxe de définition similaire. Vous introduisez des structures avec le mot-clé struct et des classes avec le mot-clé class. Les deux placent leur définition entière dans une paire d'accolades :


struct SomeStructure {
    // Définition de la structure
}
class SomeClass {
    // Définition de la classe
}
Par convention, leur nom commence par une majuscule.

struct Resolution {
    // La variable width est une propriété de la structure Resolution 
    var width = 0
    // Le type Int est implicite et donc automatiquement déduit
    var height = 0
}

class VideoMode {
    // Instance de la structure Resolution
    var resolution = Resolution() 
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

L'exemple ci-dessus définit une nouvelle structure appelée Resolution, pour décrire une résolution d'affichage basée sur les pixels. Cette structure a deux propriétés stockées appelées width et height. Les propriétés stockées sont des constantes ou des variables regroupées et stockées dans le cadre de la structure ou de la classe. Ces deux propriétés sont déduites comme étant de type Int en les définissant sur une valeur entière initiale de 0.

L'exemple ci-dessus définit également une nouvelle classe appelée VideoMode, pour décrire un mode vidéo spécifique pour l'affichage vidéo. A noter que la dernière propriété est une valeur facultative String appelée name. Cette propriété reçoit automatiquement une valeur par défaut de nil, ou "aucune namevaleur", car il s'agit d'un type optionnel.

Instance de structure et classe : création d'un objet


let someResolution = Resolution()
let someVideoMode = VideoMode()

Les structures et les classes utilisent toutes les deux la syntaxe d'initialisation pour les nouvelles instances. La forme la plus simple de syntaxe d'initialisation utilise le nom de type de la classe ou de la structure suivi de parenthèses vides, telles que Resolution() ou VideoMode(). Cela crée une nouvelle instance de la classe ou de la structure, avec toutes les propriétés initialisées à leurs valeurs par défaut.

Accéder aux propriétés

Vous pouvez accéder aux propriétés d'une instance à l'aide de la syntaxe dot. Dans la syntaxe à point, vous écrivez le nom de la propriété immédiatement après le nom de l'instance, séparé par un point (.), sans aucun espace :


print("La largeur de résolution est \(someResolution.width)")
// "La largeur de résolution est 0"

Vous pouvez explorer les sous-propriétés, telles que la propriété width dans la propriété résolution d'un VideoMode :


print("La largeur de résolution de someVideoMode est \(someVideoMode.resolution.width)")
// "La largeur de résolution de someVideoMode est 0"

Vous pouvez également utiliser la syntaxe dot pour attribuer une nouvelle valeur à une propriété de variables :


someVideoMode.resolution.width = 1280
print("La largeur est maintenant de \(someVideoMode.resolution.width)")
// "La largeur est maintenant de 1280"

Toutes les structures ont un initialiseur membre généré automatiquement, que vous pouvez utiliser pour initialiser les propriétés de membre des nouvelles instances de structure. Les valeurs initiales des propriétés de la nouvelle instance peuvent être transmises à l'initialiseur de membre par nom :


// On créer une instance de Résolution
// mais la valeur witdh sera de 640
// celle de height sera de 480

let vga = Resolution(width: 640, height: 480)

Contrairement aux structures, les instances de classe ne reçoivent pas d'initialiseur de membre par défaut.

Les structures et les énumérations sont des types de valeurs

Un type de valeur est un type dont la valeur est copiée lorsqu'elle est affectée à une variable ou constante, ou lorsqu'elle est transmise à une fonction.

En fait, tous les types de base de Swift - entiers, nombres à virgule flottante, booléens, chaînes, tableaux et dictionnaires - sont des types de valeurs et sont implémentés comme des structures en arrière-plan.

Toutes les structures et énumérations sont des types valeurs dans Swift. Cela signifie que toutes les instances de structures et d'énumérations que vous créez - et tous les types de valeurs qu'elles ont comme propriétés - sont toujours copiées lorsqu'elles sont transmises dans votre code.


let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

On modifie ensuite la propriété width de cinema.


cinema.width = 2048

La vérification de la propriété width de cinema montre qu'elle a bien changé pour être 2048 :


print("cinema a une largeur de \(cinema.width) pixels")
// "cinema a une largeur de 2048 pixels"

Cependant, la propriété width de l'instance hd d'origine a toujours l'ancienne valeur de 1920 :


print("hd est toujours de \(hd.width) pixels")
// "hd est toujours de 1920 pixels"
structure et classe swift
Classe et structure Swift - Source Apple

Le même comportement s'applique aux énumérations.

Les collections définies par la bibliothèque standard comme les tableaux, les dictionnaires et les chaînes utilisent une optimisation pour réduire le coût de performance de la copie. Au lieu de faire une copie immédiatement, ces collections partagent la mémoire où les éléments sont stockés entre l'instance d'origine et toutes les copies. Si une des copies de la collection est modifiée, les éléments sont copiés juste avant la modification. Le comportement que vous voyez dans votre code est toujours comme si une copie avait eu lieu immédiatement.

Les classes sont des types de références

Contrairement aux types valeurs, les types références ne sont pas copiés lorsqu'ils sont affectés à une variable ou constante, ou lorsqu'ils sont passés à une fonction. Plutôt qu'une copie, une référence à la même instance existante est utilisée.


let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

Ensuite, tenEighty est affecté à une nouvelle constante, appelée alsoTenEighty, et la fréquence d'images de alsoTenEighty est modifiée :


let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

Parce que les classes sont des types de références, tenEighty et alsoTenEighty, les deux font référence à la même instance VideoMode. En fait, ce ne sont que deux noms différents pour la même instance unique, comme le montre la figure ci-dessous :

structure swift
Classe et structure Swift - Source Apple

print("frameRate.property tenEighty is \(tenEighty.frameRate)")
// Prints "frameRate.property of tenEighty is now 30.0"

Cet exemple montre comment les types de références peuvent être plus difficiles à raisonner. Si tenEighty et alsoTenEighty étaient éloignés dans le code de votre programme, il pourrait être difficile de trouver toutes les façons dont le mode vidéo est changé.

Partout où vous utilisez tenEighty, vous devez également penser au code qui utilise alsoTenEighty, et vice versa. En revanche, les types de valeurs sont plus faciles à raisonner car tout le code qui interagit avec la même valeur est rapproché dans vos fichiers sources.

tenEighty et alsoTenEighty sont déclarés comme des constantes plutôt que des variables. Cependant, vous pouvez toujours modifier tenEighty.frameRate et alsoTenEighty.frameRate parce que les valeurs des constantes tenEighty et alsoTenEighty elles-mêmes ne changent pas réellement. tenEighty et alsoTenEighty eux-mêmes ne "stockent" pas l'instance VideoMode- au lieu de cela, ils se réfèrent tous deux à une instance VideoMode dans les coulisses. C'est la propriété frameRate du sous-jacent VideoMode qui est modifiée, pas les valeurs des références constantes à cela.

Opérateur d'identité

Étant donné que les classes sont des types de références, il est possible que plusieurs constantes et variables se réfèrent à la même instance unique d'une classe en arrière-plan. (Il n'en va pas de même pour les structures et les énumérations, car elles sont toujours copiées lorsqu'elles sont affectées à une constante ou une variable, ou transmises à une fonction.)

Il peut parfois être utile de savoir si deux constantes ou variables se réfèrent exactement à la même instance d'une classe. Pour cela, Swift fournit deux opérateurs d'identités :

  • Identique à : ===
  • Différent de : !==

Utilisez ces opérateurs pour vérifier si deux constantes ou variables font référence à la même instance unique :


if tenEighty === alsoTenEighty {
    print("Se réfère à la même instance VideoMode.")
}  // "Se réfère à la même instance VideoMode."
Notez que identique à (représenté par trois signes égaux, ou ===) ne signifie pas la même chose que égal à (représenté par deux signes égaux, ou ==).

Identique à signifie que deux constantes ou variables de type classe se réfèrent exactement à la même instance de classe.

Égal à signifie que deux instances sont considérées comme égales ou équivalentes en valeur.