• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 12/12/2019

Bonus : Découvrez le concept de références

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

 Hop hop hop, tu ne voulais pas nous parler d'un truc important encore ?!

Ah si ! Quand je vous ai introduit le protocole UITableViewDataSource, on a d'une part limité ce protocole à des classes, en adossant class à la déclaration de notre protocole :

protocol UITableViewDataSource: class {
    // (...)
}

Et dans notre Table View, je vous ai dit que l'on avait notre objet dataSource, mais avec le mot réservé weak :

class UITableView: UIScrollView {
    weak var dataSource: UITableViewDataSource?
}

Alors, pourquoi a-t-on fait tout cela ? Qu'est-ce que cela veut dire ? On va rapidement toucher au monde des références pour comprendre tout cela.

Les références

Dans les langages de programmation un peu modernes (comme Swift !), les objets que vous créez restent dans la mémoire, tant qu'au moins une référence existe vers cet objet.

Quand mon objet n'a plus aucun autre objet qui n'a de référence sur lui, pouf ! Il disparaît ! Sous iOS, la technologie qui fait tout cela s'appelle Automatic Reference Counting (ARC).

Dans d'autres langages, on parle de Garbage collector pour désigner cette technologie : le programme nettoie la mémoire en enlevant les objets qui ne servent plus à rien. Et comment sait-on qu'un objet ne sert plus à rien ? Quand il n'a plus de références ! Dans ce cas, mon programme n'a plus aucun moyen d'accéder à cet objet, et il est considéré comme perdu.

Tout cela ne s'applique qu'aux classes ; les structures et les énums ne sont pas concernées. D'où le petit mot class dans la déclaration de notre protocole, qui permet de garantir que seule une classe pourra adopter ce protocole.

Voilà un exemple d'une application simple qui contient une Table View et un bouton :

Les flèches en noir représentent des références.

Ici, ma Table View et mon bouton restent bien dans la mémoire, puisque j'ai au moins un objet qui les référence, mon View Controller. Et celui-ci reste aussi dans la mémoire, puisqu'il a également une référence... Et ainsi de suite.

OK, mais tout cela ne me dit pas ce qu'est une référence !

Une référence, c'est très simple ! Quand j'écris ceci :

class ListViewController: UIViewController {
    var tableView: UITableView
}

Je crée une référence de mon ListViewController vers ma propriété tableView. Tant que mon contrôleur est dans la mémoire (et généralement, il y reste tant qu'il est présent dans ma navigation), ma Table View est là aussi. C'est bien rassurant, finalement !

Le mot-clé weak

Si on reprend notre exemple de delegate de tout à l'heure, ma Table View s'écrit comme cela, si j'enlève ce fameux mot weak :

class UITableView: UIScrollView {
    var dataSource: UITableViewDataSource?
}

Donc en termes de référence, quand j'écris après dans mon View Controller tableView.dataSource = self, cela donne ça :

Et là, c'est le drame. 😱😱😱

Pourquoi c'est le drame ?

Parce que, sans faire attention, j'ai créé un retain cycle. En fait, chaque objet a une référence vers l'autre. Même si mon View Controller n'est plus dans la navigation et qu'aucun objet n'a de référence vers lui, le couple View Controller <> Table View ne disparaîtra jamais, car chacun a au moins une référence, celle de l'autre objet du couple. Cela crée finalement une fuite mémoire.

Mon Dieu, mais que va-t-on faire ??!

Pas de panique ! Vous l'aurez sans doute compris, c'est là que le mot weak entre en jeu ! Pour comprendre weak, on va d'abord regarder ce que fait son contraire : strong.

Par défaut, quand je déclare une propriété sur un objet de type Objet :

var monObjet: Objet

Cela équivaut à écrire :

strong var monObjet: Objet

Ma référence vers mon instance de Objet doit être forte pour maintenir mon objet dans la mémoire. Quand le nombre de références fortes vers mon objet tombe à zéro, il n'y a plus rien pour le garder dans la mémoire et c'est là qu'il disparaît.

Donc quand je prépose weak à la déclaration de ma variable, j'indique que je veux une référence faible. Et au contraire d'une référence forte, une référence faible ne retient pas mon objet dans la mémoire ! Je peux accéder à mon objet dans la mémoire, mais ce n'est pas moi qui le retiendrai. Je ne vais pas le retenir s'il doit disparaître.

Autrement dit, lorsque ARC compte les références pour savoir si un objet doit être supprimé de la mémoire, il ne compte que les références fortes, les faibles ne comptant pas.

Si on reprend notre schéma de tout à l'heure, avec la déclaration en weak, cela donne ça :

Et voilà le travail, le mot weak permet de briser ce fameux retain cycle ! Quand mon contrôleur ne sera plus dans la navigation, et qu'il n'aura plus de référence vers lui, ma tableView ne l'empêchera pas d'être enlevé de la mémoire.

Retour sur les outlets

Ouais, mais on ne me la fait pas. Ton schéma est faux, on a déclaré notre Table View avec un IBOutlet et avec le mot clef weak.

C'est vrai. Quand on déclare un outlet avec weak, en théorie, notre objet ne devrait même pas pouvoir rester dans la mémoire, puisqu'il n'y a aucune référence strong pour le retenir, non ?

Bien sûr, c'est faux, sinon nos apps ne fonctionneraient pas depuis le début hé hé !

Alors, que se passe-t-il au juste en réalité ? Eh bien, il suffit de se souvenir que tout bon UIViewController gère une vue principale. Si on reprend notre schéma, dans la réalité, on a ceci :

La vue principale maintient un lien fort sur l'ensemble de ses sous-vues via la propriété subviews. Et tant que mon contrôleur est présent, il maintient aussi un lien fort sur sa vue principale via la propriété view. Donc ma tableView reste bien dans la mémoire.

On déclare donc nos outlets en weak pour éviter une redondance, ou pour éviter des problèmes si on crée des liens entre objets qui n'ont rien à voir ! Depuis le temps que l'on déclare des outlets, cela devait vous démanger de ne pas savoir, non ? :)

Si tout cela vous paraît compliqué, pas de panique. Retenez simplement le concept de retain cycle, et que si deux objets s'autoréférencent, vous allez avoir des problèmes de mémoire. Lorsque vous créerez vos propres delegates, pensez à les indiquer en weak pour éviter ce problème, et tout ira bien !

En résumé

  • Un objet reste dans la mémoire, tant qu'au moins une référence forte existe vers cet objet.

  • Si deux objets s'autoréférencent avec des références fortes, cela crée un retain cycle : mes objets ne peuvent plus être nettoyés de la mémoire.

  • Le mot réservé weak permet de résoudre ce problème : les références dites faibles ne comptent pas lorsque le programme détermine si l'objet est encore utile ou pas.

  • On déclare un delegate en weak pour éviter de créer un cycle de rétention et donc une fuite mémoire.

  • Les propriétés avec des IBOutlets sont déclarées en weak : en général, l'objet aura déjà une référence forte interne et en rajouter une autre ne servira pas.

Merci à Henry Huck, relecteur de ce cours, à qui vous devez ce chapitre !

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