79 votes

Pourquoi une page de Safari se brise iOS rendu ?

Je sais le titre n'est pas qu'explicatif, mais voici l'histoire: je suis en train d'élaborer un jeu par navigateur, principalement à l'aide de JavaScript et de la Mapbox de la bibliothèque.

Tout fonctionne bien sur pc, Android et iOS, mais un problème apparaît sur iOS: après avoir laissé le jeu de fonctionner pendant quelques minutes, le téléphone soudainement commence à avoir des artefacts graphiques et de l'écran, la plupart du texte codé.

Voici quelques photos de ce que le téléphone commence à trop ressembler à: enter image description here enter image description here enter image description here

Ma question est: qu'est-ce exactement dans mon code peut en être la cause? Une fuite de mémoire? (LE: il s'est avéré en fait être une fuite de mémoire)
La vraie question est: Comment se fait que vous pouvez presque brique tout le téléphone par la simple lecture d'une page web? Ne devrait pas l'arrêt de Safari, ou au moins l'iOS ?

Ce n'est pas un problème avec cet appareil, que ce problème peut être reproduit sur différents appareils iPhone. (Je ne suis pas sûr à propos des différentes versions iOS).

Comment je peux reproduire l'erreur:

  1. Ouvrir le jeu (à l'intérieur de Safari).
  2. Le laisser fonctionner pendant 3 à 4 minutes.
  3. Glisser vers le bas le centre de notification, et tout se passe fou.
    J'ai ajouté une vidéo YouTube montrant comment je peux reproduire l'erreur (sur mon iPhone 5C).
    Il semble que la question apparaît d'abord dans le centre de notification (si vous faites glisser vers le bas le menu du haut).
    Pour l'instant, ce problème semble se produire uniquement sur iPhone 5C iOS 9.2.1 (13D15). Il se produit également sur le nouvel iOS version 9.3.

Afin de résoudre ce problème j'ai:

  1. Fermer le Safari de l'application (dans lequel le jeu de l'onglet est ouvert).
  2. Verrouiller le téléphone. Après le déverrouillage, il est tout retour à la normale.

Quelques détails sur le jeu en lui-même:

  1. Le jeu montre une Mapbox carte et certaines unités (marqueurs).
  2. Un Node.js server s'exécute à 1 tick/seconde et après chaque itération de la mise à jour du jeu de l'état est envoyé au navigateur par le biais de la Prise.io.
  3. Chaque fois que le navigateur reçoit l'état de la partie, il met à jour les marqueurs en conséquence.
  4. *Le jeu peut également mettre à jour les marqueurs si vous effectuez un zoom avant ou arrière, ou si vous les sélectionnez.

EDIT2: Trouvé la fuite de mémoire (comme prévu). Après la fixation de cette fuite (vérifiez undefined _icon) le problème ne se produit plus. Cela signifie, que quelque part le long de ces lignes, le Safari/iOS bug est déclenchée.

Voici exactement ce qui est étant appelée à chaque tick, pour chaque unité de cluster (qui était caché et regroupés avec les 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 c'est l' icons tableau:

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 appel est à partir de cette bibliothèque: https://github.com/kottenator/jquery-circle-progress

DÉMO

Yay, j'ai été en mesure de créer un jsFiddle qui reproduit le bug: https://jsfiddle.net/cte55cz7/14/ Ouvrez Safari sur l'iPhone 5C et attendez quelques minutes. Sur l'iPhone 6 et l'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 quelqu'un qui ne veut pas l'exécuter.

1voto

A STEFANI Points 261

Cette fuites de mémoire est probablement dû à la façon dont " WebKit JS Moteur œuvres [safari webkit-javascript llvm]

et ressemble vraiment à une mémoire virtuelle buffer overflow, ayant un impact direct sur le reste de la RAM (partagé et utilisé également par iOS pour stocker de l'Interface Utilisateur graphique des éléments)

Relativement à la partie de code: "[...]trouver jQuery fuites de mémoire, c'est facile. De vérifier la taille de $.le cache. Si elle est trop grande, l'inspecter et de voir les entrées de rester et pourquoi. [...]" (http://javascript.info/tutorial/memory-leaks)

Laissez-moi espérer que c'est par rapport à cette boucle for :

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 effectuée, et aussi en supposant que le fait que vous y trouverez des entrées, vous pouvez nettoyer les données manuellement avec removeData() ou vous pouvez utiliser le premier $elem.detach() et ensuite mettre $(elem).remove() dans setTimeout.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X