• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 28/05/2024

Enrichissez vos propriétés

Enrichissez vos propriétés

Pour le moment, vous connaissez deux types de propriétés :

  • les propriétés d'instance ;

  • les propriétés de classe (ou statiques).

Qu'elles soient statiques ou d'instance, toutes les propriétés que vous avez vues pour le moment sont ce qu'on appelle des propriétés stockées. Cela veut dire qu'elles sont associées à une valeur qui est stockée en mémoire.

Il existe en Swift un autre type de propriété : les propriétés calculées. Et c'est ce que nous allons voir dans ce chapitre.

Comprenez le concept des propriétés calculées

Les propriétés calculées diffèrent des propriétés stockées ainsi :

  • les propriétés stockées sont associées à une valeur ;

  • les propriétés calculées sont associées à un calcul.

Un calcul ? C'est-à-dire ?

En fait, si on y réfléchit, une propriété admet deux actions :

  • une action pour récupérer la valeur contenue, on va appeler cette action get (récupérer, en anglais) ;

  • une action pour modifier la valeur contenue, on va appeler cette action set (modifier, en anglais).

Dans une propriété stockée, ces actions ont lieu automatiquement. Dans une propriété calculée, on va pouvoir modifier les actions get et set.

Implémentez des propriétés calculées

Rien de tel qu'un bon exemple pour voir comment tout ça fonctionne. Je vous présente la classe Square suivante, qui permet de définir un carré.

class Square {
   var length = 1
}

J'aimerais bien ajouter une propriété perimeter à ma classe, qui me permettrait d'obtenir le périmètre de la classe. Le périmètre d'un carré c'est 4 fois la longueur, donc allons-y !

class Square {
   var length = 1
   var perimeter = 4
}

Hmm... C'est bien, mais le problème, c'est que si je modifie la longueur de mon carré, mon périmètre ne sera plus exact. On voit apparaître la limite des propriétés stockées. Je vais donc vous montrer comment créer une propriété calculée.

Transformons donc notre périmètre en propriété calculée :

class Square {
   var length = 1
   var perimeter: Int {
      get {
         return 4
      }
      set {
      }
   }
}

Voici la forme basique d'une propriété calculée. Pour l'instant elle a exactement le même rôle qu'avant. Détaillons un peu ce qui s'y trouve :

  • tout d'abord il faut indiquer le type de la propriété. Comme je ne lui donne plus de valeur, il faut lui préciser le type explicitement ;

  • ensuite j'ouvre des accolades, un peu comme pour une fonction ;

  • à l'intérieur des accolades se trouvent deux sortes de sous-fonctions :

    • get : c'est l'action qui permet de récupérer une valeur, on appelle ça le getter. Elle se comporte exactement comme une fonction, avec une valeur de retour de type Int . Pour l'instant, elle renvoie simplement 4,

    • set : c'est l'action qui permet de modifier notre valeur, on appelle ça le setter. Pour l'instant, cette fonction est vide. Telle quelle, elle ne change rien à ce qui se passe lorsqu'on modifie la valeur de la variable perimeter.

Commençons par modifier notre getter :

var perimeter: Int {
   get {
      return length * 4
   }
   set {
   }
}

De cette façon, le périmètre sera toujours égal à 4 fois la longueur :

let c = Square()
c.length = 4
c.perimeter // Affiche 16

Le problème maintenant, c'est lorsqu'on modifie le périmètre :

let c = Square()
c.perimeter = 8
c.length // Affiche 1

La longueur ne s'adapte pas quand le périmètre change ; pourtant les deux doivent toujours être reliés.

Je vais donc modifier le setter pour qu'il modifie la longueur à chaque fois que le périmètre est modifié :

var perimeter: Int {
   get {
      return length * 4
   }
   set {
      length = newValue / 4
   }
}

Le setter se comporte exactement comme une fonction qui a pour paramètre newValuenewValue contient la nouvelle valeur que l'on est en train de donner au périmètre. Par exemple, si j'écris :

let c = Square()
c.perimeter = 8

Dans ce cas, newValue vaut 8 ; et donc maintenant si j'affiche la longueur, j'obtiens bien 8 / 4 = 2 :

c.length // Affiche 2

Dans le setter, la propriété newValue contient la nouvelle valeur. Et la propriété perimeter contient l'ancienne valeur. Cela peut être pratique si on souhaite comparer les deux valeurs.

En résumé :

  • Le getter se comporte comme une fonction avec une valeur de retour du type de la propriété. Il est appelé lorsqu'on veut récupérer la valeur de la propriété.

  • Le setter se comporte comme une fonction avec un paramètre newValue du type de la propriété. Il est appelé lorsqu'on modifie la valeur de la propriété.

Parfois nous écrivons des propriétés que nous n'avons pas besoin de modifier. Nous ne pouvons pas pourtant en faire des constantes, car leur valeur peut varier en fonction des autres valeurs. Par exemple, ajoutons une propriété description à notre Square.

class Square {
   let description = "Je suis un carré"
   // (...)
}

C'est bien, mais c'est un peu générique. Essayons d'afficher plutôt "Je suis un carré de longueur X". Il va nous falloir une propriété calculée :

var description: String {
   get {
      return "Je suis un carré de longueur \(length)"
   }
   set {
   }
}

Voilà qui fonctionne comme on le souhaite. Mais on veut aller plus loin. On ne veut pas que quelqu'un puisse modifier la description, car il pourrait écrire n'importe quoi et notre description ne serait plus vraie.

On va transformer cette propriété calculée en propriété calculée en lecture seule, comme ceci :

var description: String {
   get {
      return "Je suis un carré de longueur \(length)"
   }
}

Il suffit d'effacer le setter, et cette propriété ne peut plus être modifiée ! On dit qu'elle est en lecture seule. Je ne peux donc plus écrire :

let c = Square()
c.description = "N'importe quoi" // error: 'description' is a get-only property
var description: String {
   return "Je suis un carré de longueur \(length)"}

Appréhendez quelques règles des propriétés calculées

Maintenant que vous avez bien compris ce que sont les propriétés calculées, je vais vous annoncer quelques règles ou remarques à leur sujet.

  1. Les propriétés calculées ne sont jamais constantes. Autrement dit, on ne peut pas les déclarer avec let . Leur valeur est calculée ; donc, par définition, elle est appelée à changer.

  2. Les propriétés calculées ont toujours un getter. Au minimum, une propriété, ça renvoie une valeur, donc il faut toujours pouvoir le faire :

// OK il y a un getter
var calculatedProperty: Type {
   get {
      return something
   }
   set {
   }
}

// OK il y a un getter, la propriété est en lecture seule
var calculatedProperty: Type {
   get {
      return something
   }
}

// OK la propriété est en lecture seule, le getter est directement dans les accolades
var calculatedProperty: Type {
   return something
}

// FAUX ! Il n'y a pas de getter, c'est interdit

var calculatedProperty: Type {
   set {
   }
}

// ARCHI FAUX ! Il n'y a rien du tout
var calculatedProperty: Type {
}

C'est tout pour les propriétés calculées !

Observez les propriétés

Il existe un moyen d'observer les propriétés en Swift.

Observer les propriétés, mais qu'est-ce que c'est encore que cette histoire ?

Vous allez voir que c'est assez proche de ce qu'on a déjà vu. Observer une propriété permet d'effectuer une action lorsque la propriété est modifiée.

Hé, mais c'est exactement comme un setter !

Eh oui, exactement ! Dans notre exemple du carré, la méthode set nous permettait de modifier la longueur dès que le périmètre était modifié. Le seul souci là-dedans, c'est queset ne fonctionne qu'avec des propriétés calculées.

Il existe deux méthodes pour écouter des propriétés stockées :

  • willSet : cette méthode est appelée juste avant la modification de la propriété ;

  • didSet : cette méthode est appelée juste après la modification de la propriété.

Prenons un exemple et essayons d'observer la propriété longueur :

var length = 1 {
   willSet {
      print("Le carré va changer de taille")
   }
   didSet {
      if oldValue > length {
         print("Le carré a rapetissé")
      } else {
         print("Le carré a grandi")
      }
   }
}

Dans la méthode willSet , on affiche que le carré va changer de taille, car cette méthode est appelée avant la modification.

Dans la méthode didSet , on compare l'ancienne valeur contenue dans la variable oldValue à la nouvelle valeur contenue dans length . En fonction du résultat, on affiche que le carré a rapetissé ou grandi. Cette méthode est appelée après la modification.

De façon similaire à oldValue , la méthode willSet a une variable newValue qui contient la nouvelle valeur. Le tableau ci-dessous résume cela :

Avec willSet, la nouvelle valeur sera dans newValue et l'ancienne valeur dans la propriété. Avec didSet, la nouvelle valeur sera dans la propriété et l'ancienne valeur dans oldValue.
willSet et didSet

Donc en résumé, pour observer la modification d'une propriété on utilise :

  • set : pour les propriétés calculées ;

  • willSet et didSet : pour les propriétés stockées.

Nous avons vu dans ce chapitre un certain nombre de notions assez avancées sur les propriétés. Pour vous récompenser pour votre persévérance et vous donner une vue d'ensemble des propriétés, je vous ai concocté un petit schéma. N'hésitez pas à y revenir en cas de doute.

Les propriétés de classe utilisent toujours le mot clé static. Les propriétés d'instance peuvent être déclarées avec le mot clé var, tout simplement.
Les différentes propriétés que nous avons vues

À vous de jouer !

Exercice 1

Observez la propriété occupiedSeats de Bus . Avant sa modification, faites afficher la phrase "Il y a du mouvement dans le bus...". Après sa modification, faites afficher la phrase "X personnes viennent de monter !" ou "X personnes viennent de descendre !" selon que la valeur de la propriété augmente ou baisse.

Exercice 2

Ajoutez une propriété calculée en lecture seule au Bus. Cette propriété s'appellera description  , et contient  "Je suis un bus conduit par X avec Y personnes dedans.". X est le nom du chauffeur et Y est le nombre de personnes dans le bus.

En résumé

  • On peut diviser les propriétés en deux types : les propriétés stockées et les propriétés calculées. Parmi les propriétés calculées, on peut créer des propriétés calculées en lecture seule.

  • Une propriété calculée permet de modifier les getters et setters au moyen des méthodes get et set .

  • Une propriété calculée n'est jamais constante, et a toujours un getter.

  • Une propriété stockée peut être observée grâce aux méthodes willSet et didSet .

Maintenant que vous savez tout sur les setters et les getters de la POO, je vous invite à aller plus loin sur la notion de l’initialisation !

Exemple de certificat de réussite
Exemple de certificat de réussite