Chargement d'un gros Widget, problème mémoire.
Bonjour à tous,
Toujours dans la série Widget, un testeur ma suggérée une très bonne idée, à savoir regrouper tous les widgets que j'ai déjà fait en un seul et pouvoir les lancer ou réduire dans le controle center via un icone.
Ce que j'ai fais et qui fonctionne pas trop mal:
En gros tous mes widgets ont été réalisés depuis le début dans des UITableViewCell :
Un widget à donc un controller avec sa table et en général une ou deux cells.
L'idée du widget qui regroupe tous les autres c'est de faire un controller avec une cell qui peut appeler toutes les cells des autres widgets. Donc je me suis dis, tout ce que j'ai à faire c'est copier coller les cellules d'un storyboard à l'autre et de charger les sous classes dans ma table de mon widget "tout en un".
Du coup je me retrouve avec une table assez balaise (30 cells dans la UITableView correspondante).
Ma cell qui permet de charger les autres peut contenir jusqu'à 8 boutons parametrables par l'utilisateur.
Donc le widget peut potentielement avoir 8 cellules chargées en mémoire...
Problème :
Certaines cells de widget sont assez gourmandes, typiquement j'ai un graphique qui charge tous les pas de l'utilisateur, un calendrier qui permet de slider sur plusieurs années avec tous vos rdv, boussoles etc etc. Donc quand je m'amuse à charger les 8... bah ça plante à 12mo de mémoire utilisée (dans le controle center) avec un petit "memory warning".
Premières idées :
-Etant débutant, tous mes logos d'icones sont des png... Donc peut-être que mes UIImageView prennent de la mémoire inutiliment. Il me semble qu'il y a des façons de créer des formes de façon moins gourmande.
-Ma façon de faire est mauvaise, et la je sais pas trop comment faire.
Donc je n'ai pas de questions en mode "pourquoi ça fait ça", mais plutot qu'est ce que vous me conseilleriez, ou qu'est ce qui vous perturbe.
PS : les widgets chargés de façon independante dans le control center fonctionnent parfaitement bien, c'est simplement le fait d'en appeler 8 "gros" via ce widget "all in one" qui peut provoquer dans certaines conditions le crash du widget.
PS 2 : j'en profite pour faire un petit appel à testeurs, les miens étant des proches, ils ne sont pas très critiques sur mon app, et me font que très peu de retours sur les bugs. Donc si ça vous interesse donnez moi votre adresse.
Voici un petit liens de ce que fait l'app, (ne faites pas trop attention aux fautes d'orthographe, je suis pas super balaise en anglais et ça sera corrigé d'ici ce soir ou demain) :
https://www.facebook.com/WidgetKit?fref=ts
Réponses
Bonsoir !
Je reviens sur mon sujet...
Pour ceux qui ont un peu la flemme de lire, en résumé j'ai un problème de gestion de mémoire : quand je charge une cellule d'un UITableView, et que je la fais disparaitre avec un classique deleterow, je ne vois aucun changement au niveau de la mémoire, comme si celle-ci était toujours allouée...
Si c'est le comportement attendu, j'aimerai justement qu'à chaque fois que je supprime une cellule, celle-ci soit désallouée.
Autre problème, quand je quitte le contrôleur, la mémoire n'est également pas désallouée. Pourtant je n'ai pas de retain cycles (enfin en tout cas, pas que je sache...).
Auriez-vous une idée ?
C'est normal, UITableView ne détruit pas la cellule, il la garde dans un coin pour la recycler ou la détruire plus tard, selon ces propres besoins de mémoire.
Pas normal, ça ..
Non, ça prend toujours la même place à l'affichage, quelque soit la manière dont l'image a été créée ou stockée sur le disque. Il est recommandé d'utiliser du png pour les icônes.
Ce qui me perturbe c'est d'imaginer toute la mémoire consommée quand les 8 widgets fonctionnent en même temps, dans un pauvre UITableView conçu pour gérer des cellules de petites tailles.
Alors je peux pas vraiment te dire jusqu'à cb ça monte vu que le widget plante à 12 Mo. Mais suivant le type de cells, je peux en charger 8 dans certains cas, et dans ces cas là ça fonctionne très bien.
PS: les widgets sont affichés un à un, quand j'en sélectionne un autre, le précédent disparait en laissant la place à celui sélectionné. C'est pour cela que j'aurais aimé désalloué celui qui est déselectionné.
Donc en gros ça donne ça :
Cellule 1
bouton widget 1 | |bouton widget 2 | ... jusqu'à 4
Cellule 2
Mon Widget
Quand j'appuie sur l'un des boutons, la cellule 2 (qui est en fait un widget) est remplacée par une nouvelle cellule (et donc nouveau widget). Donc au final je n'ai jamais 8 cellules dans ma tableView, mais au maximum 2 cellules...
oui bien sur , voici ma méthode cellForRow:
Ma façon de faire est peut-être très mauvaise, je n'ai jamais fais vérifié ma façon de coder à d'autres
Quand je dis que mes controller sont pas desalloués, c'est uniquement dans le control center. Sinon dans l'appli il y a pas de problèmes.
PS : désolé pour le parpaing
EDIT : on a posté au même moment.
Fichtre, il est bien compliqué ton code. Tu devrais prendre l'habitude de créer de petites fonctions, pour rende le code plus lisible et plus facile à tester. C'est une vraie assiette de spaghetti, là .
Est-ce que la création de tes cellules ressemblent à ça ?
Si je comprend bien, tu as une multitude de cellules avec des identificateurs de recyclage différents. Pas très bon pour le recyclage, ça.. Ah je comprend mieux. Tu utilises le nom de ta cellule dans le storyboard comme identificateur de recyclage ! C'est deux choses bien différentes. Pas étonnant que le recyclage fonctionne mal, et sature la mémoire. Tu devrais tester avec un identificateur de recyclage unique, histoire de voir si ça marche mieux.
Ahhhh ok ! C'est exactement ça, c'est pour ça que les cellules ne sont pas recyclées.
EDIT : En fait je comprends pas la différence entre identifiant de recyclage et identifiant.
Effectivement tu as bien vu, l'identifier dans mon storyboard correspond au simpleTableIdentifier.
Bon je sens que je vais aller lire la doc, j'ai pas du comprendre la première fois...
Donc j'ai testé avec un identifiant unique, mais le prob c'est que forcement il charge pas ma cellule correspondante du storyboard... donc ça coince quand j'attribue pas la suite certaines propriétés propre à la cellule.
En fait je ne comprends pas comment ça pourrait fonctionner... comment je peux dire à mon programme "réutilise cette mémoire, mais transforme la cellule correspondante en celle-ci"
- le fait que toutes les cells soit définies dans un seul xib
- le fait que les cells ayant un identifier sont conservés en memoire pour être recyclées.
Si le fait d'avoir toutes les cells en mémoire utilise trop de mémoire, alors il faut desactiver le recyclage en leur donnant un cellidentifier nul.
Tu peux avoir une approche mixte en mettant un identifier nul uniquement sur celles qui sont grosses.
Mais comment je les charge depuis le storyboard dans ce cas ? (si l'identifiant est nul)
EDIT :
avec
Tu confonds encore l'identifiant de recyclage avec l'identifiant du storyboard. Cela n'a RIEN à voir !
C'est comme si tu utilisais ton numéro de sécurité sociale quand on te demande le numéro de ton passeport, ou ton numéro de téléphone. Ce sont tous des identifiants, mais pas de même nature.
FKDEV te parle de l'identifiant de recylage des cellules, pas du Storyboard.
Tu peux utiliser "Zorglub" ou "ConfitureDeFraise" comme identifiant de recyclage pour toutes les cellules, ça marchera.
L'identifiant dans le storyboard n'est utile qu'au moment de la création de la cellule. Enfin dans ton code. Pour moi qui crée mes cellules avec du code, je n'ai qu'un identifiant de recyclage, qui peut être n'importe quoi.
Il me semble que dans le code suivant, le chargement du nib (loadNibNamed) ne sert à rien.
temp.history=[NSMutableArray arrayWithArray:[sharedDefaults objectForKey:@healthKitHistory]];
Tu mélanges deux techniques, il me semble.
Tu dois avoir une ligne de ce type aussi non ?
Une autre technique sans identifier consiste à charger un xib, et récupérer directement la cell chargée :
Bonjour à tous !
Merci pour votre aide, c'est vraiment cool et maintenant j'ai compris la différence, effectivement c'était pas du tout clair !
Du coup j'ai essayé sans identifiant de recyclage et c'est moins performant. La mémoire n'est pas immédiatement désallouée, donc ça sature plus rapidement...
Comme solution intermédiaire j'ai réduit le nombre de boutons à 4. Donc l'utilisateur ne pourra mettre que 4 widgets en raccourci... c'est un peu dommage mais c'est en attendant d'optimiser mon code... je pense qu'il en a grandement besoin.
D'ailleurs vous avez du remarquer que je charge mes préferences via shareDefaults, initialisé comme suit dans viewdidload :
Instruments me dit que j'ai une fuite de mémoire à ce niveau. Vous avez une idée du pourquoi du comment ?
Bon apparemment d'autres personnes ont ce problème :
http://stackoverflow.com/questions/27557571/nsuserdefaults-initwithsuitename-leaks-memory-in-ios-8
La seule solution est de mettre à nil le nsuserdefaults dans le dealloc du controller... Bof bof quand même, je pensais que ce genre de choses étaient gérées par l'ARC...
Ok donc pas le choix, faut bien le mettre manuellement à nil ?
Et ben, y'en a du widget la !! L'app est déjà sortie ? (pas trouvée sur google)
Nop pas encore ! elle est en validation depuis 10 jours ! Je suis assez impatient pour tout vous dire... Mais il y a de grande chance qu'elle soit rejetée
Elle fait vraiment "tout et n'importe quoi", tu voulais pas te concentrer sur certains points ?
- une app : productivity widget
- une app : fun widget (avec les jeux)
...
?
Edit : parce que la, si je recherche un widget précis, je vais choisir une app qui fait moins en me disant qu'elle le fera mieux
J'y ai effectivement pensé. Mais je pense qu'il y a deux visions :
-Soit effectivement je perds l'utilisateur (ou ne le trouve pas en fait), avec un choix de widgets trop hétérogène.
-Soit en proposant une large gamme, je peux pousser l'utilisateur à débloquer des widgets qu'il n'aurait même pas penser utiliser et qui finalement peuvent l'intéresser.
Suivant les résultats de cette version je scinderai ou non l'application.
ok ca se tient
Oui c'est complétement vrai, c'est un comportement dont j'ai un peu peur... mais mon but est de regrouper des applications petites mais utiles en une seule, c'est d'ailleurs comme ça que je communique sur le store.
Je vise donc les utilisateurs qui ont pleins d'appli aux fonctionnalités très simples. Je ne veux pas que l'utilisateur passe du temps sur l'appli en fait. Celle-ci ne servira qu'a paramétrer les widgets, leur taille, fonctionnalité.
Oui mais niveau AEO tu vas te faire défoncer :
- je cherche une calculatrice : je tape "calc"
- je cherche un compteur : je tape "counter"
etc...
T'es limité à 100 caractères, sans compter le nom de ton app (important le nom de l'app pour l'AEO) qui pour toi ne pourra être que générique.
Donc faut vraiment compter sur de la présence sur les blogs au lancement, pour avoir des DL au début et essayer d'avoir autant de commentaires 4/5 étoiles que possibles.
oui j'ai essayé d'optimiser justement, avec l'outil donné par sensor tower par exemple.
Mais clairement je pense pas pouvoir atteindre un bon ranking sur aucun de ces mots clefs : calculator (cal, calculatrice), calendar, game, counter etc etc. Même si j'en ai quand même mis certains, j'ai essayé d'être objectif sur ma réussite ^^. Je vise plutôt les mots comme widget, shortcut ou launcher avec certe moins de trafic mais moins de concurrences.
Le nom de l'app je l'ai fais un peu à ralonge : " Widget Kit - Tools and Games for your Notification Center "
Pour la présence sur les blogs je suis pas super au point également, j'ai très peu de contacts. Je vais essayer d'apparaitre dans l'article qui référence les meilleurs widgets de chez iphon.fr.
Par contre j'ai un bon réseau (école des mines) qui va envoyer une newsletter à tous les anciens. Ce qui peut être un plus.
Je pense également pouvoir générer au lancement une petite centaine d'avis positifs grâce à la mobilisation de mes proches.
Ca fait partie des critères qu'Apple a mentionné quand ils ont présenté le NotificationCenter et les widgets qu'on pouvait y mettre, en mettant bien l'accent sur le fait que ces widgets devaient être le plus optimisé possibles et éviter tant de mettre beaucoup de temps à se charger qu'éviter de prendre trop de ressources.
De plus, l'application qui est au premier plan, quand elle reçoit la notification comme quoi elle est devenue inactive à cause de l'affichage du NotificationCenter, s'attend à redevenir active peu de temps après (ou à passer complètement en background " si finalement suite à l'ouverture du NotificationCenter tu tapes sur une notif qui te fait ouvrir une autre app " mais dans ce cas elle en sera informée par une autre notification). Du coup ça peut fausser le workflow des autres application si son usage est de rester plusieurs 10aine de minutes (jeu, etc) dans le NotificationCenter au lieu des quelques secondes imaginées par un usage classique.
Enfin, cela a aussi des conséquences potentielles sur la batterie et l'usage général. Je me demande même si iOS ne killerait pas un widget s'il prend trop de mémoire et de batterie, avec une limite bien plus basse que pour une application classique, vu qu'un widget est sensé être un truc peu consommateur, rapide à consulter et qui consomme le moins possible (tu as regardé avec Instruments, en plus de l'usage mémoire de tes widgets, ce que donne l'usage batterie quand tu testes sur un device ? Je serais curieux de voir le résultat)
Bref, je pense que l'usage détourné que tu fais des widgets aura du mal à passer la validation de l'AppStore.
J'ai effectivement conscience de tous ces points. Mais je pense justement coller avec une utilisation rapide. Aucun des widgets n'est fait pour être utiliser plus d'une minute. L'idée c'est juste de raccourcir le parcours utilisateur de un ou deux clics.
Par exemple :
J'ai un widget qui donne la position de l'utilisateur. Celui-ci peut décider de la partager ou d'ouvrir map pour plus de précision via le widget... 5 sec
Le calendrier : checker ses rdv de la semaine ou du mois ( j'en ai éprouvé le besoin, je trouvais que le widget d'apple n'était pas complet/pratique) et cliquer sur un rdv pour l'ouvrir via le calendrier => 30 sec max
TicTacToe ou puissance 4 : faire une partie avec un voisin => aller...1 minute
etc
J'ai fait attention Ali, les seuls widgets qui peuvent effectivement être en désaccord avec le style d'utilisation prévu par apple sont le memory (reproduire une suite de couleur) et le chronomètre. Mais il existe plein de chronomètres en widgets, et en jeux... et bien il existe un démineur, un 2048 et flappy like en widget sur le store.
Pour ce qui est de l'utilisation mémoire c'est uniquement de ma faute, je pense que mon code est très mauvais. Pour la batterie je testerai ça ce soir et je te dirai. (Pour la mémoire, un widget est kill à partir de 12mo, aucun de mes widgets, sauf celui de ce poste, ne dépasse 4mo)
EDIT : de toute façon je serai vite fixé, la réponse demain ou après demain.
En tout cas le boulot reste impressionnant pour tous ces widgets !
Si jamais ca passe pas chez Apple, à ta place je sortirais chaque widget independement à 0,99€ pièce
Merci
J'aimerai bien faire cette solution, mais je trouve ça trop cher, pour le moment les utilisateurs auront 3 widgets débloqués d'offices et 5 étoiles pour débloquer les widgets de leur choix. En sachant que les widgets les plus "élaborés" coutent 3 étoiles, et la plus part sont à 1 ou 2 étoiles...
Il y a en ce moment 3 concurrents qui développent exactement le même principe : "Wdgts", "Vidget" et "Mes Widgets". Je les trouve tous très bons mais ne poussant pas le bouchon suffisant loin à mon sens.
Si Apple refuse je garderai l'application en local pour mes proches, à la base je les ai vraiment fait pour m'amuser et pour passer le temps, puis des amis voulaient d'autres fonctionnalités ... Donc c'est pas non plus très grave si c'est pas commercialisable
En revanche si ça intéresse certaine personne je vous la transmettrai volontiers (et même maintenant si vous voulez tester l'app)