Подтвердить что ты не робот

Почему страница Safari нарушает рендеринг iOS?

Я знаю, что название не так объяснимо, но вот история: я разрабатываю браузерную игру, в основном используя JavaScript и библиотеку Mapbox.

Все работает хорошо на настольных компьютерах, Android и iOS, но в iOS появляется одна проблема: после запуска игры в течение нескольких минут телефон внезапно начинает иметь графические артефакты и отображает большую часть текста, скремблированного.

Вот несколько фотографий того, что начинается с телефона: введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь

Мой вопрос: что именно в моем коде может вызвать это? Утечка памяти? ( LE: на самом деле это была утечка памяти)
Реальный вопрос: как получается, что вы можете почти полностью закрыть весь телефон, просто просматривая веб-страницу? Не следует ли Safari остановить это или, по крайней мере, iOS?

Это не проблема с этим конкретным устройством, так как эта проблема может быть воспроизведена на разных устройствах iPhone. (Я не так уверен в разных версиях iOS).

Как я могу воспроизвести ошибку:

  • Откройте игру (внутри Safari).
  • Пусть он работает 3-4 минуты.
  • Сдвиньте центр уведомлений, и все сойдет с ума.
    Я добавил видео YouTube, показывающее, как я могу воспроизвести ошибку (на моем iPhone 5C).
    Кажется, что проблема сначала появляется в центре уведомлений (если вы прокручиваете меню сверху).
    На данный момент эта проблема возникает только на iPhone 5C iOS 9.2.1 (13D15). Это также происходит в новой версии iOS 9.3.

Чтобы исправить эту проблему, я должен:

  • Закройте приложение Safari (в котором вкладка игры открыта).
  • Блокировка телефона. После разблокировки все возвращается к норме.

Некоторые сведения о самой игре:

  • В игре показана карта Mapbox и некоторые единицы по ней (маркеры).
  • Сервер Node.js работает со скоростью 1 тик/секунду, и после каждого тика обновленное состояние игры отправляется в браузер через Socket.io.
  • Каждый раз, когда браузер получает состояние игры, он соответствующим образом обновляет маркеры.
  • * Игра может также обновлять маркеры, если вы увеличиваете или уменьшаете масштаб или выбираете их.

EDIT2: Обнаружена утечка памяти (как и ожидалось). После исправления этой утечки (проверьте undefined _icon) проблема больше не возникает. Это означает, что где-то вдоль этих строк запускается ошибка Safari/iOS.

Вот что именно вызывается каждый тик, для каждой группы, которая была сгруппирована (была скрыта и сгруппирована с другими внутри 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);

И это массив 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 вызов из этой библиотеки: https://github.com/kottenator/jquery-circle-progress

DEMO

Yay, я смог создать jsFiddle, который воспроизводит ошибку: https://jsfiddle.net/cte55cz7/14/ Откройте Safari на iPhone 5C и подождите пару минут. На iPhone 6 и iPad mini авария страницы (как и ожидалось из-за утечки памяти)

Здесь тот же код в HasteBin, для тех, кто не хочет его запускать.

4b9b3361

Ответ 1

Эти утечки памяти, вероятно, связаны с тем, как работает "WebKits JS Engine" [safari webkit-javascript llvm]

и действительно выглядит как переполнение буфера виртуальной памяти, оказывая непосредственное влияние на оставшуюся RAM (совместно используемую и используемую iOS для хранения графических элементов пользовательского интерфейса).

Относительно части кода: "[...] найти утечки памяти jQuery легко. Проверьте размер $.cache. Если он слишком велик, проверьте его и посмотрите, какие записи остаются и почему. [...]" (http://javascript.info/tutorial/memory-leaks)

Позвольте мне ожидать, что он относится к этому для цикла:

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);
}

Предполагая, что проверка выполнена, а также при условии, что вы найдете записи, вы можете очистить данные вручную с помощью removeData() или вы можете использовать первый $elem.detach(), а затем поместите $(elem).remove() в setTimeout.