• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/1/19

Plongez dans les coulisses de l'initialisation

Log in or subscribe for free to enjoy all this course has to offer!

Nous avons vu dans les chapitres précédents que l'on peut initialiser une classe en utilisant le mot-clé init. Dans l'initialisation, on peut initialiser des propriétés et faire toute sorte de calcul pour préparer notre objet comme on le souhaite. Dans ce chapitre, nous allons voir que l'initialisation obéit à un certain nombre de règles que l'on va apprendre à respecter.

La première règle

Si vous ne retenez qu'une seule chose de ce chapitre, retenez la première règle ! La première règle de l'initialisation est la suivante :

À la fin de l'initialisation, toutes les propriétés stockées doivent être initialisées.

On peut se le refaire encore une fois, ça vaut le coup :

À la fin de l'initialisation, toutes les propriétés stockées doivent être initialisées.

Bien, qu'est-ce que ça veut dire ? Cela veut dire que lorsque l'initialisation est terminée, toutes les propriétés stockées doivent avoir une valeur. Et pour faire cela, vous savez déjà comment faire. Vous avez trois options :

  • Soit vous déclarez votre propriété avec une valeur par défaut.

  • Soit vous déclarez votre propriété de type optionnel, elle aura pour valeur nil par défaut.

  • Soit vous initialisez votre propriété dans l'initialiseur.

Cela veut dire que la première mission d'un initialiseur, c'est d'initialiser toutes les propriétés stockées qui n'ont pas de valeur. Si il ne remplit pas cette mission, votre programme va planter !

Initialisation pratique et désignée

ll existe deux types d’initialiseur. Jusqu'à présent, tous les initialiseurs que vous avez rencontrés sont du type désigné (designated). Les initialiseur désignés sont les initialiseurs principaux d'une classe.

Nous allons découvrir maintenant un deuxième type d'initialiseur : les initialiseurs pratiques (convenience). Comme leur nom l'indique, ces initialiseurs ne sont pas primordiaux pour la classe, mais ils sont pratiques. Leur rôle est de proposer une initialisation facile ou en tout cas plus facile que les initialisations prévues par les initialiseurs désignés.

Prenons tout de suite un exemple dans notre programme. Reprenons notre classe HomeRoadSection :

class HomeRoadSection: RoadSection {
    var children: Int

    init(children: Int) {
                self.children = children
        super.init(type: .home)
    }
}

Cette classe définit un initialiseur qui a en paramètre children. C'est pratique. Mais parfois, nous n'allons pas avoir envie de préciser le nombre d'enfants dans la maison. Parfois on voudrait juste lui mettre une valeur par défaut. Nous allons donc définir un initialiseur pratique pour cela.

L'initialiseur pratique se déclare avec le mot-clé convenience comme ceci :

convenience init() {
    self.init(children: 2)
}

Le mot-clé convenience permet d'indiquer la nature pratique de l'initialiseur. À l'intérieur de cette fonction, j'utilise l'initialisation désignée avec children en paramètre. Maintenant, je peux écrire  HomeRoadSection()  pour initialiser une nouvelle section. C'est plus concis et plus facile comme ça.

Il y a donc une différence d'intention entre les deux types d'initialiseur :

  • désigné : ce sont les initialisations principales

  • pratique : ils permettent des initialisations faciles pour certains cas courants

Pratique, désigné : quelles règles ?

Mais derrière cette différence d'intention se trouve une vraie réalité technique ! Cette réalité peut s'illustrer en deux règles :

  1. Un initialiseur désigné doit faire appel à un initialiseur désigné d'une classe mère si existante.

  2. Un initialiseur pratique doit faire appel à un initialiseur désigné de la même classe.

Autrement dit, dans un initialiseur désigné, on doit trouver quelque part super.init : on appelle l'initialiseur désigné de la classe mère. Et dans un initialiseur pratique, on doit pouvoir lire self.init : on appelle un initialiseur désigné de la même classe.

Pour bien comprendre tout ça, réessayons de créer un initialiseur pratique. Cette fois, faisons-le avec la classe RoadSection. On va pouvoir l'initialiser sans paramètre et obtenir que le canevas dessine une section de route simple. Pour cela, nous allons créer l'initialiseur pratique suivant :

convenience init() {
    self.init(type: .plain)
}

Cet initialiseur est bien du type pratique puique je le déclare avec le mot-clé convenience. Et il appelle l'initialiseur désigné de la même classe.

Pour bien comprendre tout ça, voici un schéma de l'initialisation de RoadSection et HomeRoadSection :

On voit bien dans ce schéma que les initialisations pratiques appellent les initialiseurs désignés de leur classe de façon horizontale. Tandis que les initialiseurs désignés appellent les initialiseurs désignés de la classe mère de façon verticale. Vous pouvez imaginer les initialiseurs désignés comme le tronc de l'initialisation de votre arbre de classe. Les initialisations pratiques n'en sont que les branches.

Vous verrez que dans le développement d'applications iPhone, vous serez souvent amené à créer des sous-classes de certaines classes proposées dans iOS. Vos sous-classes seront bien plus faciles à utiliser si vous maîtrisez les deux types d'initialisation.

Exercice

Entrainez vous !

En résumé

  • À la fin de l'initialisation, toutes les propriétés stockées doivent être initialisées.

  • Il existe deux types d'initialiseur :

    • désigné : ce sont les initialisations principales

    • pratique : ils permettent des initialisations faciles pour certains cas courants

  • Un initialiseur désigné doit faire appel à un initialiseur désigné d'une classe mère si existante. Un initialiseur pratique doit faire appel à un initialiseur désigné de la même classe.

Example of certificate of achievement
Example of certificate of achievement