Gestion des crashes à cause de la pression sur la mémoire
Bonjour à tous,
Je voudrais m'attaquer à la partie "mémoire sous pression" de mon app.
Jusqu'à maintenant mon app tient le coup, avec une utilisation mémoire qui tourne autour de 75Mb.
Mais, lorsqu'elle passe à l'arrière plan et que je lance safari pour faire une recherche par exemple, elle plante et un message s'affiche dans Xcode qui me dit que c'est à cause de la mémoire.
Je voudrais comprendre ce qui se passe (et régler le problème), et avant de me lancer là -dedans, je voulais avoir vos lumières :
1/ j'imagine que tout se passe dans - (void)didReceiveMemoryWarning. Qu'est-ce que je dois y faire typiquement ? Je ne peux pas dealloc le VC... Que s'y passe-t-il ? Les VCs tombent-ils les uns après les autres ? Ou bien, tout tombe d'un coup et l'appli et relaunchée ? En l'occurrence, dans mon cas, quand je rouvre l'appli, je me retrouve sur l'écran d'accueil de mon app.
Réponses
À ce moment, il faut te débarrasser de toutes les ressources que tu peux recréer, typiquement des images.
Je ne pense pas qu'une approche systématique soit bonne. La question est de savoir où ça bouffe de grosses quantités de RAM pour pouvoir agir à ces endroits précis.
Merci de ta réponse @Céroce.
Je crois que pour l'instant, je n'ai pas trop envie de gérer ça (dealloc des images et refetch) : ça impliquerait que je change mon archi. Ce qui est possible en revanche, c'est que je dézingue les VCs qui prennent trop de place (et revenir aux écrans précédents).
J'aimerais aussi comprendre ce qu'il se passe en fait quand la mémoire manque (dealloc? ou crash total et relance de l'app en tâche de fond)
Ce qui est également bizarre c'est que la mémoire sature une fois que tu as quitté l'application et non pendant, tu sais d'où ça vient ?
La mémoire sature parce qu'il lance une autre application (Safari) qui a besoin de mémoire. Donc soit il obtempère quand le système lui dit de faire de la place, soit son application est tuée.
On ne quitte jamais une application. Tu crois la quitter, mais iOS se contente de la placer en tâche de fond.
euh oui je voulais dire mettre en fond ^_^
ah ok ! Donc en fait iOS notifie les app en fond quand une active utilise trop de mémoire ?
Idéalement quand tu lances une app, tu reviens où tu étais quand tu l'as quittée. Sachant que quitter peut signifier répondre à un appel téléphonique ou autre interruption.
Pour faire cela, le plus simple est de garder les apps en mémoire. En pratique la memoire etant limité, ona plusieurs degrzdation de ce principe, la premiere etant de demander à l'app de liberer ce qui n'est pas indispensable au maintien de l'etat de l'app.
Donc si tu as une app qui affiche une image correspondant à un état, tu peux libérer l'image mzis conserver l'etat.
Si la pression devient trop forte le systeme te previent qu'il va te tuer pour que tu puisses sauvegarder cet état pour y revenir au lancement. En pratique on sait bien que c'est tres difficile sur des apps complexes ou les VC ont ete empiles.
Pour repondre a ta question suivant le principe ci-dessus, il ne faut surtout pas libérer les controller (qui vont contenir des états), mais plutot les vues. Idealement tu dois pouvoir liberer tout ce qui a ete alloué dans viewdidload, puis liberer la vue elle-même : self.view.
Quand le controller sera reafficher, il recreeda toutes les vues en fonctikn de l'etat qu'il a consrrvé.
Si je comprends bien, ce n'est pas didReceiveMemoryWarning que je dois faire le nettoyage mémoire mais avant (dans un méthode genre applicationWillGoBackground). En pratique, comment fais-tu pour libérer la vue ?
Si je suis ton raisonnement, un autre problème risque d'être soulevé : quid de l'image qui sera affichée lorsque le système iOS présente toutes les apps ouvertes ?
Au final, j'ai l'impression que ça va être compliqué...
Quand iOS présente toutes les applications ouvertes, il affiche en fait des copies d'écran réalisées avant que l'application n'entre en tâche de fond. C'est automatique, tu n'as pas à t'en occuper.
Si l'application a été détruite par iOS pour récupérer de la mémoire, il se contente d'afficher la copie d'écran, faisant croire à l'utilisateur qu'elle est toujours là . Et peut la redémarrer discrétement si l'utilisateur veut s'en servir de nouveau.
Non c'est bien dans didReceiveMemoryWarning, c'est-à -dire uniquement quand le système a besoin de mémoire, pas la peine d'anticiper les besoins du système.
Un exemple de cette fonction sur iOS 6 et + (attention c'est assez différent sur iOS < 6)
Attention au fait que self.view va recréer la vue et provoquer l'appel de viewDidLoad si la vue a déjà été libérée, c'est pour cette raison qu'on utilise self.isViewLoaded.
Je présume que ça doit se mettre dans chaque Contrôleur ? Et que iOS envoie le message didReceiveMemoryWarning à tous les contrôleurs présents en mémoire ?
une doc Apple sur le sujet parmi tant d'autres
J'ai observé que quand je passe mon app à l'arrière plan, l'empreinte mémoire diminue drastiquement !
Intéressant à savoir !! Je pense que cette diminution de mémoire se fait en dehors des messages type didReceiveMemoryWarning
Par exemple c'est le cas de NSCache qui est justement fait pour ça
ça ne m'étonnerai pas non plus que des classes comme UIImage et les images créés par imageNamed se libèrent automatiquement de la mémoire quand elles reçoivent la notification et rechargent l'image ensuite quand tu reviens (et que tu en as de nouveau besoin, sans s'embêter à recharger les images qui ne sont plus a l'écran tant que tu ne les redemandes pas). Genre de chose qui serait tout à fait logique car une bonne façon de faire pour être un bon citoyen vis à vis de l'utilisation mémoire... et expliquerait parfaitement ton comportement que tu décris.