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

79

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

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

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

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

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

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

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

Чтобы решить эту проблему, мне нужно:

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

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

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

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

ДЕМО

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

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

XCS
источник
2
PS: Размещено на SO после того, как был отклонен за размещение вопроса SuperUser. Я надеюсь, что это подходящее место, чтобы задать этот вопрос.
XCS
2
@wottle Я тестировал свой iPhone 5C, и другой человек тестировал (на другом континенте: D) на другом iPhone, но я думаю, что его модель также 5C (и он был тем, кто на самом деле рассказал мне об этих артефактах). Завтра буду тестировать на iPhone 6 и iPad mini.
XCS
2
Не воспроизводится на моем iPhone 6. Это может быть ошибка компьютерной графики. Можете ли вы опубликовать всю информацию о версии ios вместе с информацией об оборудовании?
Fresheyeball,
2
Вот Это Да! И я подумал, что на Windows 10 Mobile разрывается экран…
BoltClock
6
А) Я даже не злюсь. Это восхитительно. Б) Мне интересно, можно ли это на самом деле использовать в качестве эксплойта, поскольку вы явно обращаетесь к памяти, которую не должны иметь, и В) Я думаю, что интересно, что все всегда наклоняется влево. Это наводит меня на мысль, что по какой-то причине вы увеличиваете или шаг (иначе говоря, шаги) некоторых текстур или изменяете логику шага графического процессора. Определенно ошибка Safari / iOS / прошивки.
0x24a537r9 05

Ответы:

1

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

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

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

Позвольте мне ожидать, что это относительно этого цикла 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);
}

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

СТЕФАНИ
источник