Dans ce chapitre, je vous propose de résoudre notre erreur ! Et pour cela, nous allons avoir besoin d'investiguer le code !
Dans le chapitre précédent, l'analyse de la pile d'exécution nous a révélé que l'erreur avait lieu dans la fonction updateAllSetsScore
. Voici le code de celle-ci :
private func updateAllSetsScore(forPlayer player: Player) {
for i in 0..<6 {
if i < match.sets.count {
let set = match.sets[i]
playerSetScoreLabels[player]![i].text = "\(set.scores[player]!)"
} else {
playerSetScoreLabels[player]![i].text = ""
}
}
}
Hmm... Difficile de trouver l'erreur du premier coup d'œil dans cette fonction. Pour nous aider, nous allons utiliser un point d'arrêt !
Les points d'arrêt
Le principe d'un point d'arrêt (breakpoint en anglais) est très simple. Il permet, comme son nom l'indique, d'interrompre l'exécution de notre code à la ligne qui nous intéresse. Nous allons voir dans ce chapitre et dans le suivant que c'est extrêmement pratique pour repérer les problèmes.
Mais pour résoudre notre erreur, nous allons avoir besoin pour l'instant d'un point d'arrêt un peu particulier et très puissant !
Le point d'arrêt d'exception
Le point d'arrêt d'exception (ou exception breakpoint) permet d'interrompre le code dès que le programme rencontre une erreur qui va faire planter l'application.
Pour créer ce point d'arrêt, il faut aller dans le navigateur des points d'arrêt. C'est l'avant-dernier onglet du navigateur (panneau de gauche) :
Cet onglet est vide pour le moment, car vous n'avez pas encore créé de points d'arrêt. Pour ajouter notre fameux point d'arrêt d'exception, allez tout en bas de l'interface, cliquez sur le +
et choisissez dans la pop-up "Exception Breakpoint..." :
Ça y est ! Votre point d'arrêt d'exception est créé et il apparaît dans le navigateur de points d'arrêt :
Pour que vous puissiez admirer la puissance de ce point d'arrêt, vous n'avez plus qu'à relancer votre application et vous allez voir l'exécution s'interrompre juste avant notre erreur.
Naviguez dans la pile d'exécution
Le point d'arrêt vous conduit directement jusqu'au fichier et même à la ligne qui pose problème :
Nous n'avons même pas besoin d'analyser la pile d'exécution. On est directement conduit au bon endroit.
En plus, grâce au point d'arrêt, votre pile d'exécution s'affiche sur la gauche dans le navigateur de débogage et vous pouvez naviguer à l'intérieur !
Pour cela, il vous suffit de cliquer sur la ligne de votre choix sur la droite dans la pile d'exécution et vous êtes immédiatement conduit à la ligne de code correspondante.
La vue des variables
Bon c'est bien beau de s'amuser, mais on la résout cette erreur ?
Oui... Mais ne soyez pas rabat-joie non plus :D ! La ligne qui semble poser problème est la suivante :
playerSetScoreLabels[player]![i].text = ""
On débarque un peu dans ce code qu'on ne connaît pas donc pour savoir un peu qui est qui, laissez-moi vous présenter la vue des variables ! Dans la zone de débogage, vous avez deux panneaux. A gauche, c'est la vue des variables et à droite la console que vous connaissez déjà.
Cette vue permet de visualiser toutes les variables existantes au point d'arrêt où nous nous trouvons. En l'occurrence, il y a ici :
player
: c'est le paramètre de la fonction. On voit qu'il est de typePlayer
et vautone
.i
: c'est le compteur de la boucle for qui vaut ici5
.self
: c'est la classe dans laquelle nous nous trouvons, donc iciViewController
.
On peut déplier self
pour inspecter l'état de toutes ses propriétés. Notamment on peut regarder la propriété playerSetScoreLabels
qui intervient dans la ligne qui pose problème.
On comprend que son type est : [Player: [UILabel]]
donc un dictionnaire qui a comme clé un joueur et comme valeur un tableau de labels. Mais le reste n'est pas très lisible !
Afficher les variables dans la console
Heureusement, nous avons une autre option pour voir nos variables et ça se passe juste à côté, dans la console !
Enfin pas tout à fait vide ! On y voit l'inscription (lldb)
, c'est un Low Level Debugger. C'est un peu comme un terminal, on peut écrire des commandes et l'ordinateur nous répond. On peut lui poser plein de questions pour comprendre l'état de notre application.
Nous allons apprendre une seule commande, mais qui est de loin la plus utile, et elle se nomme sobrement po
pour Print Object.
Essayons-la avec notre mystérieuse variable playerSetScoreLabels
:
La console nous affiche plein de détails sur l'objet et cette fois, c'est assez clair. Il y a deux clés : Player.one et Player.two et chacune contient 5 labels. Si on va un peu plus loin, on peut écrire :
On a bien un tableau qui contient 5 labels.
Résoudre l'erreur
Maintenant, résumons-nous et je pense qu'on va pouvoir résoudre notre erreur. L'erreur intervient à la ligne suivante :
playerSetScoreLabels[player]![i].text = ""
On sait que ce playerSetScoreLabels[player]!
est un tableau de 5 éléments. On essaye donc d'accéder à son élément à l'index i
, qui vaut 5. Or le plus grand index d'un tableau de 5 éléments, c'est 4. Donc il n'y a rien à l'index 5 !
Et voilà notre erreur ! Notre boucle va un coup trop loin ! Il nous suffit de réduire l'intervalle d'un cran !
for i in 0..<6 { // <= ici il faut mettre 5 à la place du 6 !
// [...]
}
Faites la modification, lancez l'application et... ça marche ! Le bug a été éliminé ! Bravo !
En résumé
Pour identifier la source des erreurs à l'exécution, je vous suggère de créer un point d'arrêt d'exception qui vous emmène directement à la ligne qui pose problème.
Un point d'arrêt permet d'interrompre l'exécution du code et d'inspecter les variables pour identifier un bug.
Pour inspecter les variables suite à un arrêt, on peut utiliser la vue des variables, ou la commande
po
dans la console.
Dans le prochain chapitre, nous allons apprendre à positionner des points d'arrêt manuellement pour naviguer dans la pile d'exécution.