Je sais que le titre n'est pas si explicatif, mais voici l'histoire: je développe un jeu par navigateur, principalement en utilisant JavaScript et la bibliothèque Mapbox.
Tout fonctionne bien sur le bureau, Android et iOS mais un problème apparaît sur iOS: après avoir laissé le jeu fonctionner pendant quelques minutes, le téléphone commence soudainement à avoir des artefacts graphiques et à afficher la plupart du texte brouillé.
Voici quelques photos de ce à quoi ressemble trop le téléphone:
Ma question est: qu'est-ce qui exactement dans mon code peut provoquer cela? Une fuite de mémoire? ( [~ # ~] le [~ # ~] : il s’agit en fait d’une fuite de mémoire)
La vraie question est: comment se fait-il que vous puissiez presque brique l'ensemble du téléphone en parcourant simplement une page Web? Safari ne devrait-il pas arrêter cela, ou du moins iOS?
Ce n'est pas un problème avec cet appareil spécifique, car ce problème peut être reproduit sur différents appareils iPhone. (Je ne suis pas sûr des différentes versions d'iOS).
Comment je peux reproduire l'erreur:
iPhone 5C
iOS 9.2.1 (13D15). Cela se produit également sur la nouvelle version iOS 9.3.Afin de résoudre ce problème, je dois:
Quelques détails sur le jeu lui-même:
EDIT2: Trouvé la fuite de mémoire (comme prévu). Après avoir corrigé cette fuite (recherchez undefined
_icon), le problème ne se produit plus. Cela signifie que quelque part dans ce sens, le bug Safari/iOS est déclenché.
Voici ce que l'on appelait exactement chaque tick, pour chaque unité qui était en cluster (était cachée et groupée avec d'autres à l'intérieur d'un MarkerCluster):
var $icon = $(marker._icon); // marker._icon is undefined because of the clustering
$icon.html('');
$icon.append($('<img class="markerIcon" src="' + options.iconUrl + '" />'));
var iconX = 10;
var iconY = -10;
var iconOffset = 0;
for(var v in this.icons) {
this.icons[v].css('z-index', + $icon.css('z-index') + 1);
this.icons[v].css('transform', 'translate3d(' + iconX + 'px,'
+ (iconY + iconOffset) + 'px,' + '0px)');
iconOffset += 20;
this.icons[v].appendTo($icon);
}
// Fire rate icons
this.attackRateCircle = $('<div class="circle"></div>');
this.attackRateCircle.circleProgress({
value: 0,
size: 16,
fill: { color: "#b5deff" },
emptyFill: 'rgba(0, 0, 0, 0.5)',
startAngle: -Math.PI / 2,
thickness: 4,
animation: false,
});
this.attackRateCircle.hide();
// Create and display the healthbar
this.healthBar = $('<div>').addClass('healthBar ');
this.healthBar.css('z-index', $icon.css('z-index'));
this.healthBarFill = $('<span class="fill">');
this.healthBar.append(this.healthBarFill);
$icon.append(this.healthBar);
$icon.append(this.attackRateCircle);
Et voici le tableau icons
:
this.icons = {
attack_order: $('<img src="img/attack.png" class="status_icon">'),
attack: $('<img src="img/damage.png" class="status_icon icon_damage">'),
hit: $('<img src="img/hit.png" class="status_icon icon_hit">'),
};
circleProgress
l'appel provient de cette bibliothèque: https://github.com/kottenator/jquery-circle-progress
Oui, j'ai pu créer un jsFiddle qui reproduit le bug: https://jsfiddle.net/cte55cz7/14/ Ouvrez sur Safari sur iPhone 5C et attendez quelques minutes. Sur iPhone 6 et iPad mini, la page se bloque (comme prévu en raison de la fuite de mémoire)
Voici le même code dans un HasteBin, pour tous ceux qui ne veulent pas l'exécuter.
Cette fuite de mémoire est probablement due au fonctionnement du "moteur JS de WebKit" [safari webkit-javascript llvm]
et ressemble vraiment à un débordement de mémoire virtuelle, ayant un impact direct sur le RAM (partagé et également utilisé par iOS pour stocker les éléments graphiques de l'interface utilisateur)
Relativement au morceau de code: "[...] trouver des fuites de mémoire jQuery est facile. Vérifiez la taille de $ .cache. S'il est trop grand, inspectez-le et voyez quelles entrées restent et pourquoi. [...]" ( http://javascript.info/tutorial/memory-leaks )
Je m'attends à ce que cela soit relatif à cela pour la boucle :
for(var v in this.icons) {
this.icons[v].css('z-index', + $icon.css('z-index') + 1);
this.icons[v].css('transform', 'translate3d(' + iconX + 'px,'
+ (iconY + iconOffset) + 'px,' + '0px)');
iconOffset += 20;
this.icons[v].appendTo($icon);
}
En supposant que l'inspection est terminée, et en supposant également que vous trouviez les entrées, vous pouvez nettoyer les données manuellement avec removeData () ou vous pouvez utiliser d'abord $ elem.detach () puis mettez $ (elem) .remove () dans setTimeout.