La zone de débogage est une fonctionnalité utile dans le travail des développeurs iOS dans Xcode. Dès que nous commençons à maîtriser le développement pour iOS, et essayons de nous éloigner de la méthode d'impression familière et bien-aimée, et de trouver des méthodes plus rapides et plus pratiques pour comprendre l'état du système dans une certaine période, nous commençons à étudier la zone de débogage.
Très probablement, dans le débogage du panneau, vos yeux tomberont avant de comprendre ce qui se passe exactement là-bas. Lorsque l'application se bloque pour la première fois, le menu inférieur s'ouvre automatiquement, il peut initialement aider à comprendre le problème (rappelez-vous le bon vieux "Erreur fatale: Index hors de portée"), fondamentalement au tout début, vous ne comprendrez pas ce que Xcode veut de nous et google erreurs, mais au fil de la croissance, de plus en plus d'informations deviendront claires.
Dès le début, le programmeur essaie d'optimiser son travail. Pour ce faire, nous nous efforçons de comprendre à quel moment notre programme est entré dans un état incorrect. Et ici, selon le point où se situe l'évolution du programmeur, les méthodes peuvent varier. Tout d'abord, comment le débogage est effectué correctement par la méthode «print ()», puis les points d'arrêt sont placés et les méthodes «po» sont appelées, puis la familiarisation avec l'entrée de variable de débogage (la zone à côté de la console dans Xcode), puis vient la compréhension de la façon de compiler le code lors de l'arrêt Méthodes de point d'arrêt - «expression» (Au moins, c'était l'évolution que j'avais).
Essayons différentes manières qui nous aideront à comprendre et à changer l'état de notre application. Les plus simples comme «print ()» et «po» ne seront pas pris en compte, je pense que vous comprenez déjà leur essence et savez comment l'appliquer.
Créons une application simple avec un écran dans lequel nous n'aurons qu'un seul type de cellules (TableViewcell) avec deux éléments à l'intérieur: UIImageView et UILabel. Dans les cellules, nous écrirons son numéro de série et mettrons image1 ou image2 dans l'image.
La méthode tableViewCellForRowAtIndexPath créera des cellules pour nous, déposera des données et renverra:

Cette méthode va générer le tableau suivant:

Point d'arrêt
Arrêtons notre programme et ajoutons du texte à notre étiquette.
1. Définissez le point d'arrêt:

2. Le programme a arrêté l'exécution sur la ligne 55, immédiatement après l'affectation du texte. Puisque nous sommes sur une ligne située dans le champ de vision de la cellule, nous pouvons interagir avec notre cellule.
3. Nous écrivons dans la console une commande pour changer le texte de la cellule:

4. Nous supprimons notre point d'arrêt et appuyons sur le bouton "continuer l'exécution du programme".
5. Sur l'écran de notre téléphone, nous voyons que tout s'est bien passé:

expression exécute l'expression et renvoie une valeur sur le thread actuel.
Point d'arrêt modifié
Mais que se passe-t-il si nous devons modifier le texte dans un grand nombre de cellules? Ou avons-nous déjà réalisé dans le processus d'exécution du programme que nous devons changer?
Nous pouvons optimiser l'exécution de cette opération et accélérer un peu le travail, modifier le texte en cellules lorsqu'il atteint Breakpoint et continuer à exécuter le programme, cela économisera beaucoup de temps et nous permettra de ne pas imprimer la même chose pour chaque cellule.
Pour ce faire, nous devons modifier légèrement notre Breakpoint, y ajouter un code supplémentaire, qui changera son texte dans la zone de visibilité de notre cellule et continuer le programme.
- Créez un point d'arrêt.
- Faites un clic gauche sur la flèche du point d'arrêt.
- Cliquez sur Modifier le point d'arrêt.
- Condition - conditions dans lesquelles Breakpoint fonctionnera, maintenant nous n'en avons plus besoin.
- Ignorer - combien de fois ignorer Breakpoint avant que cela fonctionne (pas non plus).
- Mais l'action est ce dont nous avons besoin, sélectionnez le type d'action Debugger Command.
- Nous écrivons l'expression que nous devons exécuter:
- expression cell.desriptionTextOutlet.text = "\ (indexPath.item) mission terminée".
- Cochez - Poursuivre l'exécution après la réussite de la commande.

9. Nous essayons.

C'est un succès, il s'est avéré changer le texte de chaque cellule lors de la formation du tableau, et nous n'avons pas eu à sacrifier de temps et à prescrire des opérations pour chacune.
Fonction de point d'arrêt
Il y a toujours des moments où quelque chose se passe dans notre application que nous ne pouvons pas expliquer, le texte ne change pas ou change plus que nécessaire, il semblerait que Breakpoint dans ce cas n'a nulle part où mettre. Mais ce n'est pas entièrement vrai, si vous connaissez Obj-C et que vous savez quelle méthode le compilateur exécute que vous souhaitez suivre, vous pouvez y placer Breakpoint et la prochaine fois que la méthode est appelée, l'application s'arrête dans le processus d'exécution du code Assembler.
1. Dans le navigateur Point d'arrêt, sélectionnez Point d'arrêt symbolique.

2. Nous voulons suivre la méthode de définition du texte dans la cellule, écrivez - [UILabel setText:].

3. L'argument zéro n'existe pas et le décompte commence à partir du premier. La première méthode capturée n'est pas ce dont nous avons besoin (nous mettons l'heure actuelle à la barre d'état), mais la seconde est juste la nôtre:
4. Sous «$ arg1», la description de l'objet est stockée.
5. Sous «$ arg2», la fonction de sélection est stockée.
6. Sous «$ arg3», le texte obtenu par la méthode est stocké.
Ok, cela semble clair. Mais parfois, il existe des situations où il ne se limite pas à définir un texte dans la barre d'état, et vous devez suivre l'exécution de la méthode dans un contrôleur spécifique, que faire? Vous pouvez activer Breakpoint similaire à ce que nous avons installé précédemment, mais en définissant sa position dans le code. Qu'est-ce que cela signifie? Nous savons avec certitude que notre vue apparaîtra lorsque nous installerons le texte dans la cellule, donc la chose même est de le mettre dans viewDidLoad ou après avoir créé la cellule.
Pour créer un point d'arrêt, nous le définissons sur la ligne, et dans l'action, nous écrivons le code suivant:
breakpoint set --one-shot true --name "-[UILabel setText:]”
breakpoint set —one-shot true
- créer un point d'arrêt
—name
est le nom du point d'arrêt du caractère
“-[UILabel setText:]”
appelée méthode
Voici ce qui s'est passé:

Passer les lignes
Mais que se passe-t-il si nous soupçonnons qu'une ligne de code gâche tout notre programme? Pendant l'exécution de code, vous pouvez éviter d'exécuter une ligne de code spécifique comme celle-ci:
- Nous mettons un point d'arrêt sur une ligne que nous ne voudrions pas exécuter.
- Lorsque l'exécution s'arrête, faites-la glisser sur la ligne avec laquelle nous voulons continuer l'exécution du programme (drôle, mais cela ne fonctionne pas toujours, l'option sans glisser est plus faible).
Il existe également une autre option qui optimisera le saut de ligne - c'est la prescription de la commande appropriée dans le «point d'arrêt d'édition». L'équipe est risquée, car l'essence de ces sauts est de nous sauver de la reconstruction, mais si vous sautez l'initialisation de l'objet et essayez de le contacter, le programme se bloquera.

Arrêtons notre programme lors de l'initialisation de l'image, et nous n'allons pas du tout affecter l'image à la cellule, pour cela, nous devons ignorer cinq lignes de code et renvoyer la cellule sans image, pour cela, nous ignorons l'exécution des cinq lignes de code suivantes sur le thread actuel, et continuons le programme:

Cela semble assez bon, mais je veux toujours assigner une image, ajoutons une méthode d'affectation au point d'arrêt:

Une bonne combinaison, maintenant nous n'avons qu'un seul type d'image dans chaque cellule.
Watchpoint
Une autre fonction pratique du débogueur est le suivi des valeurs dans le programme, les points d'observation. Watchpoint est quelque peu similaire à KVO, nous avons défini un point d'arrêt pour changer l'état d'un objet, et chaque fois qu'il change son état, le processus d'exécution du programme s'arrête, et nous pouvons voir la valeur et les endroits d'où et par qui la valeur a été modifiée. Par exemple, je mets un point d'observation sur une cellule pour savoir ce qui se passe lorsqu'un tableau est paginé et qu'une nouvelle cellule est initialisée. La liste des commandes s'est avérée très grande, je ne vais donc pas en mentionner quelques-unes: exécution de la vue de mise en page à l'intérieur de la cellule et définition de la contrainte, animation, définition des états de la cellule et bien plus encore.
Pour définir le point de surveillance sur une valeur, vous devez arrêter le programme de point d'arrêt dans la portée des propriétés que vous souhaitez surveiller, sélectionnez la propriété dans le panneau «variable de débogage» et sélectionnez surveiller «<paramètre>».

Afin de supprimer le point d'observation avec une variable, vous devez regarder dans le navigateur de point d'arrêt, là avec le reste du point d'arrêt sera notre point d'observation.
Modification de l'interface utilisateur du point d'arrêt
Parfois, nous devons en savoir plus sur l'objet que nous essayons de repousser. L'option la plus simple consiste à utiliser «po» pour afficher des informations sur l'objet et à regarder l'emplacement de l'objet en mémoire au même endroit. Mais il arrive que nous n'ayons pas de lien direct vers un objet; il n'est pas représenté dans la vue API, sur laquelle repose ou est éventuellement caché par la bibliothèque. L'une des options consiste à utiliser la hiérarchie des vues, mais ce n'est pas toujours pratique et il n'est pas toujours difficile de comprendre que vous avez trouvé la vue souhaitée. Vous pouvez essayer d'utiliser la commande:
expression self.view.recursiveDescription()
C'est dans Obj-C, mais il a été supprimé dans Swift en raison des particularités du langage, nous ne pouvons pas le faire, mais puisque Debuger travaille avec Obj-C, en théorie, il peut alimenter cette commande, et il comprendra ce que vous voulez de lui . Pour exécuter le code Obj-C dans la console, vous devez entrer la commande:
expression -l objc -O - - [`self.view` recursiveDescription]
Que voyez-vous ici? Je vois une construction plutôt gênante à laquelle on pourrait s'habituer avec le temps, mais nous ferions mieux de ne pas le faire, mais utilisez des typologies pour simplifier la commande:
command alias poc expression -l objc -O —
Maintenant, notre équipe se réduit et se simplifie, mais continue de faire le travail:
poc [`self.view` recursiveDescription]
Cela fonctionnera-t-il après la fermeture de Xcode ou dans un autre projet? Hélas, non. Mais cela peut être réparé! En créant le fichier .lldbinit et en y entrant notre alias. Si vous ne savez pas comment, voici les instructions sur les articles:
1. Créez un fichier .lldbinit (vous pouvez prendre .gitignore comme prototype, il appartient au même type de fichiers texte invisibles).
2. Écrivez exactement la commande suivante dans ce fichier:
command alias poc expression -l objc -O - -
3. Placez le fichier dans le dossier par l'adresse «MacintoshHD / Users / <Voici le nom de votre utilisateur>».
Et nous avons donc obtenu une description de toutes les vues présentées à l'écran. Essayons de voir ce que nous pouvons faire avec l'adresse des objets en mémoire. Pour Swift, il existe une méthode avec un inconvénient, vous devez toujours convertir le type de l'objet en mémoire à une certaine valeur:
po unsafeBitCast(0x105508410, to: UIImageView.self)
Maintenant, nous pouvons voir la position de notre image dans la cellule, déplaçons-la pour qu'elle soit centrée sur la cellule et ait un retrait sur le côté de 20 px.

Parfois, une modification n'est pas immédiatement perceptible, mais il est nécessaire de supprimer l'application du débogage afin de remarquer la modification.
Mais si nous voulons voir quelque chose de similaire dans chaque cellule, nous devons accélérer l'exécution des commandes, nous pouvons écrire plusieurs scripts en Python qui fonctionneront pour nous (comment ajouter des scripts peut être trouvé ici
www.raywenderlich.com/612-custom-lldb-commands-in- pratique ), et si vous savez comment gérer Python et que vous voulez écrire dessus pour lldb, vous serez utile.
J'ai décidé d'écrire une extension pour la classe UIView, qui va simplement déplacer la vue dans la bonne direction, il me semblait qu'il y aurait moins de problèmes de connexion de nouveaux scripts à LLDB et pas difficile pour n'importe quel programmeur iOS (sinon vous devez apprendre Python pour LLDB).
Je n'ai pas cherché la place de l'objet en mémoire et je ne l'ai pas amené à la classe souhaitée, pour que je prenne un cadre plus tard, cela prendrait aussi trop de temps. La question a été résolue en écrivant une fonction dans l'extension UIView:

Malheureusement, cela ne fonctionne pas bien avec les cellules, probablement en raison du fait qu'au moment où la commande flush a été exécutée, toutes les positions des cellules n'ont pas été calculées et n'apparaissaient pas à l'écran (nous n'avons pas encore renvoyé tableViewCell). Avec d'autres éléments statiques, cela fonctionne bien.
Connaissant la position de la vue dans la hiérarchie, nous pouvons y accéder et changer sa position.
Et maintenant, la situation inverse est lorsque nous pouvons accéder à ViewHierarchy et que nous voulons obtenir des données de vue à partir de là. Dans Xcode, il est possible d'afficher la hiérarchie des vues lors de l'exécution d'un programme, vous pouvez également afficher les couleurs, la disposition, les types et les liaisons à d'autres objets, y compris cela. Essayons d'accéder aux contraintes de notre UIImageView.
Pour obtenir des données de contrainte:
1. Cliquez sur la hiérarchie des vues de débogage.
2. Activez le contenu coupé dans le panneau en bas de l'écran qui apparaît.
3. Activez les contraintes dans le même panneau.
4. Sélectionnez Contrainte.
5. Dans le menu, cliquez sur Edition -> Copier (Commande + C).
6. La liaison de ce type est copiée: ((NSLayoutConstraint *) 0x2838a39d0).
7. Et maintenant, tout comme nous le modifions à travers le code, vous pouvez également le changer dans lldb:
expression [((NSLayoutConstraint *)0x2838a39d0) setConstant: 60]
8. Après avoir cliqué sur le bouton Continuer, l'élément mettra à jour sa position à l'écran.
Vous pouvez modifier les couleurs, le texte, etc. de la même manière:
expression [(UILabel *)0x102d0a260] setTextColor: UIColor.whiteColor]
Le projet de démonstration s'est avéré trop simple (60 lignes de code dans le ViewController), la plupart du code que j'ai écrit est présenté dans l'article, il n'y aura donc aucune difficulté à reproduire le projet de test.
PS: Si vous avez des questions ou des commentaires, écrivez. Jetez un œil à WWDC et Debate as Pro.
Je vous conseille également de vous familiariser avec les matériaux:
Inspiré par Advanced Debugger WWDC 18 SessionCommandes du débogueurAjout de scripts Python à LLDB Xcode