Levée de la conscience : usage de mémoire & fuites
En aidant d'autres à résoudre leurs éditions de projet de cocos2d pendant l'année passée il est devenu évident que beaucoup de projets ont au moins un problème important d'une de ces régions :
- direction de mémoire
- direction de ressource
- structure codée
Exemples
Les éditions de direction de mémoire varient normalement d'allouer trop de mémoire, en chargeant trop de textures en haut le devant qui vont seulement être nécessaires plus tard, ou par les fuites de mémoire telles que les scènes pas deallocating en échangeant des scènes. Les problèmes de direction de ressource varient de ne pas ajouter les bonnes ressources à la bonne cible, en ayant pour résultat souvent la grandeur App augmentée parce que les ressources sont ajoutées au paquet, mais n'utilisées jamais par l'App. Cela pourrait vouloir dire aussi de charger des dossiers de ressource identiques sauf qu'ils ont de différents noms de fichier (les copies ?), en finissant la mémoire supplémentaire. Ou pas les lutins fermement faisants les valises dans les Atlas de Texture mais plutôt l'utilisation d'un Atlas de Texture par objet de jeu – pendant que c'est compréhensible d'un point de vue de seperation logique il gaspille vraiment des occasions pour l'optimisation. It could also mean loading identical resource files except that they have different filenames (copies?), using up additional memory. Or not tightly packing sprites into Texture Atlases but instead using one Texture Atlas per game object – while this is understandable from a standpoint of logical seperation it does waste opportunities for optimization.
Finalement, la structure codée ou le manque mènent de cela régulièrement à “tout dans une classe” le design codé qui est le fort probablement un processus évolutionniste, plutôt qu'intentionnel. Il est assez ordinaire de voir des classes avec des milliers de lignes de code, quelquefois même 10 000 lignes passées allant de code dans une classe. D'autres choses utilisent trop de CCLayers sans eux ajoutant un avantage clair, par exemple juste pour grouper tous les noeuds à un ordre de z spécifique ensemble ou les grouper par la fonctionnalité, eg une couche pour les ennemis, un pour les joueurs, un pour le fond, un pour UI, un pour le score, un pour les effets de particule, et cetera – sans n'importe laquelle de ces couches étant utilisées pour à ce quoi ils sont vraiment bons : le fait de modifier des noeuds multiples immédiatement, comme le mouvement, le fait d'escalader, le fait de faire tourner ou la z-recommande eux. Et évidemment il y a la copie & l'enfer de colle, les grands blocs de code reproduit dans les endroits différents seulement pour modifier quelques paramètres au lieu de créer une méthode qui prend les paramètres modifiables comme les arguments. Même les professionnels j'ai travaillé avec reçu si utilisé pour l'action qu'il est devenu difficile juste de surmonter la résistance du fait de laisser vont de vieilles habitudes. Mais ils ont appris. Other things are using too many CCLayers without them adding a clear benefit, for example just to group all nodes at a specific z order together or to group them by functionality, eg one layer for enemies, one for players, one for background, one for UI, one for score, one for particle effects, and so on – without any of these layers being used for what they’re really good at: modifying multiple nodes at once, like moving, scaling, rotating or z-reordering them. And of course there’s the copy & paste hell, large blocks of code reproduced in various places only to modify some parameters instead of creating a method which takes the modifiable parameters as arguments. Even professionals I worked with got so used to doing that it became hard just to overcome the resistance of letting go of old habits. But they learned.
Résumé
Rien de ce design codé et structurant ne me frappe comme bizarre ou surprenant. J'ai écrit le code comme cela moi-même. Je crois aussi si c'est assez bon et les travaux, alors pourquoi bon Dieu non ? C'est une affaire d'expérience et c'est seulement avec l'expérience que vous voyez clairement comment améliorer des choses. Cela se réalise à la courbe d'apprentissage régulière où seulement l'entraînement et les leçons particulières et juste le fait de faire simplement des erreurs et le fait d'apprendre d'eux aident à la longue. C'est comment nous apprenons des choses. I also believe if it’s good enough and works, then why the hell not? It’s a matter of experience and it’s only with experience that you clearly see how to improve things. This boils down to the regular learning curve where only training and tutoring and just simply making mistakes and learning from them helps in the long run. That’s how we learn things.
D'autre part, les choses comme la direction de Ressource et de Mémoire peuvent aussi être apprises mais ils ont une différente nature. Ils peuvent être statistiquement évalués, ils pourraient être calculés et vérifiés automatiquement. Cela me fait me demander s'il n'y a pas quelque automation et outils d'information qui aideraient des promoteurs à accomplir de meilleurs résultats du point de vue de l'usage de mémoire et de la direction de ressource ? Entre-temps c'est tout de la levée de la conscience … This makes me wonder if there isn’t some kind of automation and information tools that would help developers achieve better results in terms of memory usage and resource management? In the meantime it’s all about raising awareness …
Levée de la Conscience de Mémoire
De la manière la plus importante je crois que nous avons besoin de lever plus de conscience à ces éditions aux promoteurs cocos2d. Un pas vers cela serait pour cocos2d pour afficher un “comptoir de mémoire disponible” le long du comptoir de FPS. J'avais l'habitude de rapiécer CCDirector pour simplement afficher la mémoire au lieu de FPS depuis que c'était toujours plus important pour moi. Le type cocos2d le promoteur Joseph m'a envoyé sa version pour afficher tous les deux – je n'ai pas pensé simplement à l'évident. Ainsi si vous voudriez voir FPS et mémoire disponible à côté de l'un l'autre je crois que vous pouvez manipuler les changements dans CCDirector exposé ici : I used to patch CCDirector to simply display memory instead of FPS since that was always more important to me. Fellow cocos2d developer Joseph sent me his version to display both – I simply didn’t think of the obvious. So if you’d like to see FPS and available memory next to each other I think you can handle the changes to CCDirector outlined here:
//CCDirector.h, ajoutent au-dessous de @interface : + (double) getAvailableBytes; + (double) getAvailableKiloBytes; + (double) getAvailableMegaBytes; //CCDirector.m, ajoutent comme approprié : #include <sys/sysctl.h> #import <mach/mach.h> #import <mach/mach_host.h> [...] + (double) getAvailableBytes { vm_statistics_data_t vmStats; mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT; kern_return_t kernReturn = host_statistics (mach_host_self (), HOST_VM_INFO, (host_info_t) &vmStats, &infoCount); si (kernReturn! = KERN_SUCCESS) { rendez NSNotFound; } revenez (vm_page_size * vmStats.free_count); } + (double) getAvailableKiloBytes { revenez [CCDirector getAvailableBytes] / 1024.0; } + (double) getAvailableMegaBytes { revenez [CCDirector getAvailableKiloBytes] / 1024.0; } [...] - (vide) showFPS { cadres ++; accumDt + = dt; si (accumDt> CC_DIRECTOR_FPS_INTERVAL) { frameRate = frames/accumDt; cadres = 0; accumDt = 0; //le seul changement dans showFPS est cette ligne : NSString *str = [[NSString alloc] initWithFormat:@" le %.1f le %.1f", frameRate, [CCDirector getAvailableMegaBytes]]; [FPSLabel setString:str]; [libération de str]; } }
La levée de la conscience aux Scènes fuyantes
En plus j'hautement, fortement et avec le renforcement extrême (sans retirer un fusil) recommande aux promoteurs cocos2d de fréquemment vérifier les méthodes dealloc de votre scène. Ajoutez de préférence un point d'arrêt là, ou ajoutez au moins la ligne notante : CCLOG (”dealloc : le %”, moi). Si vous voulez une méthode plus visible mais moins indiscrète vous pourriez faire quelque chose comme le solin de l'écran ou le jeu d'un son chaque fois que la dernière scène est deallocated, pour que vous deveniez si utilisés pour cela que quand vous ne le voyez pas ou n'entendez plus il lève immédiatement votre attention. CCLOG(@”dealloc: %@”, self). If you want a more visible but less intrusive method you could do something like flashing the screen or playing a sound whenever the last scene is deallocated, so that you get so used to it that when you’re not seeing or hearing it anymore it immediately raises your attention.
Si n'importe quand pendant le développement de votre projet on n'appelle pas la méthode dealloc pour une scène quand vous changez des scènes, vous divulguez la mémoire. Divulguer la scène entière est une fuite de mémoire de la pire sorte. Vous voulez l'attraper tôt pendant que vous pouvez revenir encore sur vos pas qui pourraient avoir provoqué le problème. Dès que vous arrivez à l'utilisation des centaines d'actifs et des milliers de lignes de code et vous rendez compte ensuite que la scène n'est pas deallocated, vous serez dans pour un trajet amusant essayant de trouver où cela vient de. Dans ce cas, en enlevant des noeuds en les non remarquant jusqu'à ce que vous puissiez vous rapprocher sur le coupable est probablement la meilleure stratégie, à côté de l'utilisation des Instruments (que je n'ai pas trouvé trop utile dans ces cas). You want to catch that early while you can still retrace your steps that might have caused the problem. Once you get to using hundreds of assets and thousands of lines of code and then realize the scene isn’t deallocated, you’ll be in for a fun ride trying to figure out where that’s coming from. In that case, removing nodes by uncommenting them until you can close in on the culprit is probably the best strategy, next to using Instruments (which I haven’t found too helpful in those cases).
J'ai heurté un tel problème une fois parce que je passais l'objet de CCScene aux sous-classes pour qu'ils aient l'accès aux méthodes de la scène. La sous-classe a retenu la scène et a été tirée de CCNode et ajoutée au CCScene comme l'enfant. Le problème avec cela : pendant le nettoyage de la scène il a enlevé correctement tous les noeuds d'enfant mais certains des noeuds d'enfant retenaient encore la scène. À cause de cela on n'a jamais appelé leur méthode dealloc et à tour de rôle la scène n'était jamais deallocated. The problem with that: during cleanup of the scene it correctly removed all child nodes but some of the child nodes still retained the scene. Because of that their dealloc method was never called, and in turn the scene was never deallocated.









