Astuces de performance iOS pour rendre votre application plus performante
À propos de l'auteur
Axel est un développeur de logiciels aspirant à devenir indépendant, basé à Kuala Lumpur, en Malaisie.
Pour en savoir plus sur Axel …
De bonnes performances sont essentielles pour offrir une bonne expérience utilisateur. Les utilisateurs d'iOS ont souvent de grandes attentes vis-à-vis de leurs applications. Une application lente et ne répondant pas pourrait amener les utilisateurs à abandonner l’utilisation de votre application ou, pire, à laisser une mauvaise note.
Bien que le matériel iOS moderne soit suffisamment puissant pour gérer de nombreuses tâches complexes et intensives, le périphérique risque de ne pas répondre si vous ne faites pas attention à la performance de votre application. Dans cet article, nous examinerons cinq astuces d'optimisation qui donneront à votre application une plus grande réactivité.
1. Cellule réutilisable de la file d'attente
Vous avez probablement utilisé tableView.dequeueReusableCell (withIdentifier: for:)
à l'intérieur de tableView (_: cellForRowAt:)
auparavant. Vous êtes-vous déjà demandé pourquoi vous deviez suivre cette API peu pratique au lieu de simplement passer un tableau de cellules? Examinons le raisonnement suivant.
Supposons que vous ayez une vue sous forme de tableau avec mille lignes. Sans utiliser de cellules réutilisables, nous devrions créer une nouvelle cellule pour chaque ligne, comme suit:
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Crée une nouvelle cellule à chaque appel de cellForRowAt.
let cell = UITableViewCell ()
cell.textLabel? .text = "Cell (indexPath.row)"
cellule de retour
}
Comme vous l’auriez peut-être pensé, cela ajoutera un millier de cellules à la mémoire de l’appareil lorsque vous faites défiler vers le bas. Imaginez ce qui se passerait si chaque cellule contenait un UIImageView
et beaucoup de texte: le chargement simultané de tous ces éléments risquerait de saturer la mémoire de l'application! En dehors de cela, chaque cellule nécessiterait une nouvelle mémoire pour être allouée lors du défilement. Si vous faites défiler rapidement une vue sous forme de tableau, de nombreux petits morceaux de mémoire seront alloués à la volée, ce qui rendra l'interface utilisateur janky!
Pour résoudre ce problème, Apple nous a fourni le dequeueReusableCell (withIdentifier : pour:)
méthode. La réutilisation de cellule fonctionne en plaçant dans une file d'attente la cellule qui n'est plus visible à l'écran. Lorsqu'une nouvelle cellule est sur le point d'être visible à l'écran (par exemple, la cellule suivante en dessous lorsque l'utilisateur fait défiler l'écran vers le bas), la vue sous forme de tableau récupère une cellule de cette file d'attente et la modifie dans la méthode cellForRowAt indexPath:
.

En utilisant une file d’attente pour stocker des cellules, l’affichage sous forme de tableau n’a pas besoin de créer un millier de cellules.
En utilisant dequeueReusableCell
nous pouvons réduire la mémoire utilisée par l'application et la rendre moins sujette à l'épuisement de la mémoire! 19659015] Obtenir le flux de travail juste n'est pas une tâche facile. Il en va de même pour les estimations. Ou alignement entre les différents départements. C’est la raison pour laquelle nous avons mis en place la session «Voici comment je travaille» – avec des cookies intelligents partageant ce qui leur convient le mieux. Une partie de Smashing Membership bien sûr.
2. Utilisation d'un écran de lancement qui ressemble à l'écran initial
Comme indiqué dans les Directives sur l'interface humaine de Apple (HIG), les écrans de lancement peuvent être utilisés pour améliorer la perception de la réactivité d'une application:
destiné à améliorer la perception de votre application aussi rapidement que possible et qu'il est immédiatement prêt à être utilisé. Chaque application doit fournir un écran de lancement. ”
L’utilisation d’un écran de lancement comme écran d’accueil pour afficher la stratégie de marque ou pour ajouter une animation de chargement est une erreur courante. Concevez l’écran de lancement de manière à ce qu’il soit identique au premier écran de votre application, comme l’a mentionné Apple:
«Concevez un écran de lancement presque identique au premier écran de votre application. Si vous incluez des éléments qui ont une apparence différente à la fin du lancement de l'application, vous risquez de subir un flash désagréable entre l'écran de lancement et le premier écran de l'application.
«L'écran de lancement n'est pas une opportunité de création de marque. Ne créez pas une expérience de saisie ressemblant à un écran de démarrage ou à une fenêtre "À propos de". N'incluez pas de logos ni d'autres éléments de marque, sauf s'ils constituent une partie statique du premier écran de votre application. ”
L'utilisation d'un écran de lancement à des fins de chargement ou de marquage peut ralentir le moment de la première utilisation et rendre l'utilisateur inutile. l'impression que l'application est lente
Lorsque vous démarrez un nouveau projet iOS, un vide LaunchScreen.storyboard
est créé. Cet écran sera présenté à l'utilisateur pendant que l'application charge les contrôleurs de vue et la mise en page.
Pour que votre application soit plus rapide, vous pouvez concevoir l'écran de lancement de manière à ce qu'il ressemble au premier écran (contrôleur de vue) qui sera affiché.
Par exemple, l'écran de lancement de l'application Safari est similaire à sa première vue:

Le scénario de l'écran de démarrage est comme n'importe quel fichier de scénario, sauf que vous ne pouvez utiliser que les classes UIKit standard, telles que UIViewController, UITabBarController et UINavigationController. Si vous essayez d'utiliser d'autres sous-classes personnalisées (telles que UserViewController), Xcode vous avertira que l'utilisation de noms de classe personnalisés est interdite.

Une autre chose à noter est que UIActivityIndicatorView
ne s'anime pas lorsqu'il est placé sur l'écran de lancement, car iOS génère une image statique à partir du storyboard de l'écran de lancement et l'affiche à l'utilisateur. (Ceci est mentionné brièvement dans la présentation 2014 de la WWDC «« Les plateformes de l'état de l'Union », vers 01:21:56
.)
Le HIG d'Apple nous recommande également de ne pas inclure de texte. sur notre écran de lancement, car celui-ci est statique et vous ne pouvez pas localiser le texte en fonction des langues.
Lectures recommandées : Application mobile avec reconnaissance faciale Caractéristique: Comment créer C'est réel
3. Restauration d'état pour les contrôleurs de vue
La conservation et la restauration d'état permettent à l'utilisateur de revenir exactement au même état de l'interface utilisateur juste avant de quitter l'application. Parfois, en raison d'une mémoire insuffisante, le système d'exploitation peut avoir besoin de supprimer votre application de la mémoire lorsque celle-ci est en arrière-plan, et l'application peut perdre le suivi de son dernier état d'interface utilisateur si elle n'est pas conservée, ce qui pourrait éventuellement amener les utilisateurs à perdre leur travail. en cours!
Dans l'écran multitâche, vous pouvez voir une liste d'applications qui ont été mises en arrière-plan. Nous pourrions supposer que ces applications fonctionnent toujours en arrière-plan; en réalité, certaines de ces applications pourraient être détruites et redémarrées par le système en raison de la mémoire requise. Les instantanés d'applications que nous voyons dans la vue multitâche sont en fait des captures d'écran prises par le système dès la sortie de l'application (pour accéder à l'écran d'accueil ou multitâche).

iOS utilise ces captures d'écran pour donner l'illusion que l'application est toujours en cours d'exécution ou affiche toujours cette vue particulière, alors que l'application a peut-être déjà été arrêtée ou redémarrée en arrière-plan tout en affichant la même capture d'écran.
Avez-vous déjà vu, lors de la reprise d'une application à partir de l'écran multitâche, que l'application affiche une interface utilisateur différente de l'instantané présenté dans la vue multitâche? En effet, l’application n’a pas mis en œuvre le mécanisme de restauration de l’état et les données affichées ont été perdues lors de la suppression de l’application en arrière-plan. Cela peut donner lieu à une mauvaise expérience, car l'utilisateur s'attend à ce que votre application soit dans le même état qu'au moment de la quitter.
Extrait de l'article d'Apple :
"Ils s'attendent à ce que votre application soit dans le même état que quand ils l'ont quitté. La préservation et la restauration d'état garantissent que votre application retrouve son état précédent lorsqu'elle sera relancée. ”
UIKit déploie de nombreux efforts pour nous simplifier la conservation et la restauration de l'état: il gère automatiquement l'enregistrement et le chargement de l'état d'une application, le cas échéant. fois. Il suffit d'ajouter une configuration pour indiquer à l'application de prendre en charge la préservation et la restauration de l'état et d'indiquer à l'application quelles données doivent être conservées.
Pour permettre la sauvegarde et la restauration de l'état, nous pouvons implémenter ces deux méthodes dans AppDelegate.swift
:
application func (_ application: UIApplication, codeur shouldSaveApplicationState: NSCoder) -> Bool {
retourne vrai
}
application func (_ application: UIApplication, codeur ShouldRestoreApplicationState: NSCoder) -> Bool {
retourne vrai
}
Cela indiquera à l’application de sauvegarder et de restaurer automatiquement l’état de l’application.
Nous indiquerons ensuite à l’application quels contrôleurs de vue doivent être conservés. Pour ce faire, nous spécifions le «ID de restauration» dans le storyboard:

Vous pouvez également cocher la case «Utiliser l'aperçu du storyboard» pour utiliser l'ID du storyboard en tant qu'ID de restauration.
Pour définir l'ID de restauration dans le code, vous pouvez utiliser la propriété restoreIdentifier
du contrôleur de vue. .
// ViewController.swift
self.restorationIdentifier = "MainVC"
Pendant la conservation de l'état, l'état de tout contrôleur de vue ou vue auquel un identificateur de restauration a été attribué est enregistré sur disque.
Les identificateurs de restauration peuvent être regroupés pour former un chemin de restauration. Les identificateurs sont regroupés à l'aide de la hiérarchie de vues, du contrôleur de vue racine au contrôleur de vue actif actuel. Supposons qu'un MyViewController soit incorporé dans un contrôleur de navigation, qui est incorporé dans un autre contrôleur de barre d'onglets. En supposant qu'ils utilisent leurs propres noms de classe en tant qu'identificateurs de restauration, le chemin de restauration ressemblera à ceci:
TabBarController / NavigationController / MyViewController
Lorsque l'utilisateur quitte l'application avec MyViewController comme contrôleur de vue actif, ce chemin est enregistré par l'application. puis l'application se souviendra de la hiérarchie précédente affichée (contrôleur de navigation → → contrôleur de navigation → ).
Après avoir attribué l'identifiant de restauration, nous devrons implémenter les méthodes encodeRestorableState (avec coder et decodeRestorableState (avec coder
pour chacun des contrôleurs de vue préservés. Ces deux méthodes nous permettent de spécifier quelles données doivent être sauvegardées ou chargées et comment les encoder ou les décoder.
Voyons le contrôleur de vue:
// MyViewController.swift
Le plus grand centre commercial
// MARK: Restauration de l'état
// UIViewController est déjà conforme au protocole UIStateRestoring par défaut
extension MyViewController {
// sera appelé lors de la conservation de l'état
redéfinit func encodeRestorableState (avec le codeur: NSCoder) {
// encode les données que vous voulez sauvegarder pendant la conservation de l'état
coder.encode (self.username, forKey: "nom d'utilisateur")
super.encodeRestorableState (avec: codeur)
}
// sera appelé lors de la restauration de l'état
redéfinit func decodeRestorableState (avec le codeur: NSCoder) {
// décode les données sauvegardées et les charge lors de la restauration de l'état
si let restoreUsername = coder.decodeObject (forKey: "nom d'utilisateur") comme? Chaîne {
self.username = nom d'utilisateur restauré
}
super.decodeRestorableState (avec: codeur)
}
}
N'oubliez pas d'appeler l'implémentation de la super-classe au bas de votre propre méthode. Cela garantit que la classe parente a la possibilité de sauvegarder et de restaurer l'état.
Une fois le décodage terminé, applicationFinishedRestoringState ()
sera appelé pour indiquer au contrôleur de vue que l'état a été restauré. Nous pouvons mettre à jour l'interface utilisateur du contrôleur de vue avec cette méthode.
// MyViewController.swift
Le plus grand centre commercial
// MARK: Restauration de l'état
// UIViewController est déjà conforme au protocole UIStateRestoring par défaut
extension MyViewController {
...
remplacer la fonction applicationFinishedRestoringState () {
// met à jour l'interface utilisateur ici
self.usernameLabel.text = self.username
}
}
Voilà! Ce sont les méthodes essentielles pour implémenter la conservation et la restauration d'état pour votre application. N'oubliez pas que le système d'exploitation supprime l'état enregistré lors de la fermeture forcée de l'application par l'utilisateur afin d'éviter de rester bloqué au cas où quelque chose ne fonctionnerait pas si l'état de conservation et de restauration de l'état ne fonctionnait pas.
, ne stockez aucune donnée de modèle (c’est-à-dire des données qui auraient dû être enregistrées dans UserDefaults ou Core Data) dans l’état, bien que cela puisse sembler pratique. Les données d'état sont supprimées lorsque l'utilisateur force votre application, et vous ne voulez certainement pas perdre les données de modèle de cette façon.
Pour vérifier si la conservation et la restauration d'état fonctionnent correctement, procédez comme suit:
- Construire et lancez une application à l'aide de Xcode.
- Accédez à l'écran avec la préservation et la restauration d'état que vous souhaitez tester.
- Retournez à l'écran d'accueil (en glissant vers le haut ou double-cliquant sur le bouton principal, ou en appuyant sur Maj. ⇧ + Cmd + H dans le simulateur) pour envoyer l'application à l'arrière-plan.
- Arrêtez l'application dans Xcode en appuyant sur le bouton.
- Lancez à nouveau l'application et vérifiez si l'état a été restauré avec succès.
Cette section ne couvrant que les bases de la préservation et de la restauration de l'état, je recommande les articles suivants de Apple Inc. pour une connaissance plus approfondie de la restauration de l'état: [19659069] Conservation et restauration de l'État [19659070] Processus de préservation de l’UI
4. Réduisez au maximum l'utilisation de vues non-opaques
Une vue opaque est une vue sans transparence, ce qui signifie qu'aucun élément d'interface utilisateur placé derrière elle n'est pas visible du tout. Nous pouvons définir une vue comme étant opaque dans Interface Builder:

Ou nous pouvons le faire par programme avec la propriété isOpaque
de UIView:
view.isOpaque = true
Si vous optez pour une vue opaque, le système de dessin optimisera les performances de dessin tout en rendant l'écran.
Si une vue est transparente (c’est-à-dire que la valeur alpha est inférieure à 1,0), iOS devra alors effectuer un travail supplémentaire pour calculer ce qui devrait être fait. être affichés en mélangeant différentes couches de vues dans la hiérarchie des vues. Par contre, si une vue est définie sur opaque, le système de dessin la mettra en évidence et évitera le travail supplémentaire de fusion des multiples couches de vue qui se trouve derrière elle.
Vous pouvez vérifier quelles couches sont en cours de fusion ( Débogage → Calques de couleurs mélangées .

Après avoir coché l'option Color Blended Layers vous pouvez voir que certaines vues sont en rouge et d'autres en vert. Le rouge indique que la vue n'est pas opaque et que son affichage en sortie est le résultat de la fusion des calques derrière celle-ci. Le vert indique que la vue est opaque et qu'aucun mélange n'a été fait.

Les étiquettes illustrées ci-dessus («Afficher les amis», etc.) sont surlignées en rouge car lorsqu'une étiquette est déplacée vers le story-board, sa couleur d'arrière-plan est définie sur transparente par défaut. Lorsque le système de dessin compose l'affichage près de la zone d'étiquette, il demande le calque situé derrière l'étiquette et effectue des calculs.
Pour optimiser les performances des applications, vous pouvez également réduire le nombre de vues mises en surbrillance en rouge. possible.
En remplaçant label.backgroundColor = UIColor.clear
par label.backgroundColor = UIColor.white
nous pouvons réduire la fusion des calques entre l'étiquette et la couche de vue située derrière.

Vous avez peut-être remarqué que, même si vous avez défini un UIImageView sur opaque et lui avez attribué une couleur d'arrière-plan, le simulateur reste affiché en rouge dans la vue de l'image. Ceci est probablement dû au fait que l'image que vous avez utilisée pour la visualisation d'image comporte un canal alpha.
Pour supprimer le canal alpha d'une image, vous pouvez utiliser l'application Aperçu pour créer un duplicata de l'image ( Shift ⇧ + Cmd ⌘ + S ), et décochez la case "Alpha" lors de la sauvegarde.
![Décochez la case "Alpha" lorsque vous enregistrez une image pour supprimer le canal alpha. 19659012] Décochez la case "Alpha" lors de l'enregistrement d'une image pour rejeter le canal alpha. (<a href=](https://i0.wp.com/blog.arcoptimizer.com/wp-content/uploads/2019/02/1549369726_799_astuces-de-performance-ios-pour-rendre-votre-application-plus-performante.png?w=660&ssl=1)
5. Transférer les fonctions de traitement lourd aux threads d'arrière-plan (GCD)
Etant donné qu'UIKit ne fonctionne que sur le thread principal, le traitement intensif du thread principal ralentit l'interface utilisateur. UIKit utilise le fil principal non seulement pour gérer et répondre aux entrées de l'utilisateur, mais également pour dessiner l'écran.
Pour rendre une application réactive, il est essentiel de déplacer autant de tâches de traitement lourdes que possible en arrière-plan. Évitez les calculs complexes, la mise en réseau et les opérations d'E / S lourdes (lecture et écriture sur le disque, par exemple) sur le thread principal.
Vous avez peut-être déjà utilisé une application qui est soudainement devenue insensible à votre saisie tactile, et vous avez l'impression qu'elle l'a accroché. Cela est probablement dû au fait que l'application exécute de lourdes tâches de calcul sur le thread principal.
Le thread principal alterne généralement entre les tâches UIKit (telles que la gestion des entrées utilisateur) et certaines tâches légères à intervalles rapprochés. Si une tâche lourde est exécutée sur le thread principal, UIKit devra attendre que la tâche lourde soit terminée avant de pouvoir gérer la saisie tactile.

Par défaut, le code dans les méthodes de cycle de vie du contrôleur de vue (telles que viewDidLoad) et les fonctions IBOutlet sont exécutés sur le thread principal. Pour déplacer des tâches de traitement lourdes vers un thread d'arrière-plan, nous pouvons utiliser les files d'attente Grand Central Dispatch fournies par Apple.
Voici le modèle de commutation de files d'attente:
// Basculez vers le thread d'arrière-plan pour effectuer des tâches lourdes. tâche.
DispatchQueue.global (qos: .default) .async {
// Effectue une lourde tâche ici.
// Revenez au thread principal pour effectuer une tâche liée à l'interface utilisateur.
DispatchQueue.main.async {
// Mettre à jour l'interface utilisateur.
}
}
Le qos
signifie “qualité de service”. Des valeurs de qualité de service différentes indiquent des priorités différentes pour les tâches spécifiées. Le système d'exploitation allouera plus de temps processeur et de débit d'E / S d'alimentation pour les tâches allouées dans des files d'attente avec des valeurs de QoS supérieures, ce qui signifie qu'une tâche se terminera plus rapidement dans une file d'attente avec des valeurs de QoS supérieures. Une valeur QoS plus élevée consommera également plus d'énergie car elle utilise plus de ressources.
Voici la liste des valeurs QoS de la priorité la plus élevée à la plus basse:
![Valeurs de qualité de service de la file d'attente, triées par performance et efficacité énergétique [19659012] Valeurs de la qualité de service de la file d'attente, triées par performance et par efficacité énergétique (<a href=](https://i0.wp.com/blog.arcoptimizer.com/wp-content/uploads/2019/02/1549369726_842_astuces-de-performance-ios-pour-rendre-votre-application-plus-performante.png?w=660&ssl=1)
Apple a fourni à un tableau pratique contenant des exemples de valeurs de QoS à utiliser pour différentes tâches.
Il convient de garder à l'esprit que tout le code UIKit doit toujours être exécuté sur le thread principal. La modification d'objets UIKit (tels que UILabel
et UIImageView
) sur le thread d'arrière-plan pourrait avoir une conséquence inattendue, comme si l'interface utilisateur ne se mettait pas à jour, un crash se produirait, etc.
Extrait de l'article d'Apple :
«La mise à jour d'une interface utilisateur sur un fil autre que le fil principal est une erreur courante qui peut entraîner des mises à jour manquantes, des défauts visuels, des corruptions de données et des plantages.»
I recommande de regarder La vidéo WWDC 2012 d'Apple sur l'accès concurrentiel à l'interface utilisateur pour mieux comprendre comment créer une application réactive.
Notes
Le compromis de l'optimisation des performances est qu'il vous faut écrire plus de code ou configurer davantage paramètres au-dessus de la fonctionnalité de l'application. Cela pourrait rendre votre application livrée plus tard que prévu et vous aurez plus de code à gérer à l'avenir, et plus de code signifie potentiellement plus de bugs.
Avant de passer du temps à optimiser votre application, demandez-vous si l'application est déjà lisse ou non. si elle a une partie insensible qui doit vraiment être optimisée. Consacrer beaucoup de temps à optimiser une application déjà fluide pour gagner 0,01 seconde pourrait ne pas en valoir la peine, car il serait préférable de passer plus de temps à développer de meilleures fonctionnalités ou d'autres priorités.
Ressources supplémentaires

Source link