web-dev-qa-db-ja.com

SafariのページがiOSレンダリングを中断するのはなぜですか?

タイトルはそれほど説明的なものではないことは知っていますが、ストーリーは次のとおりです。主にJavaScriptとMapboxライブラリを使用して、ブラウザーゲームを開発しています。

すべてがデスクトップで正常に動作し、AndroidおよびiOSですが、iOSで1つの問題が発生します。

携帯電話の始まりの写真もいくつかあります: enter image description hereenter image description hereenter image description here

私の質問は次のとおりです:私のコードで正確に何がこれを引き起こす可能性がありますか?メモリリーク? ([〜#〜] le [〜#〜]:実際にメモリリークであることが判明しました)
本当の質問は:ウェブページを閲覧するだけで電話全体をほとんどレンガにすることができるのはなぜですか? Safariはこれを、または少なくともiOSを停止すべきではありませんか?

この問題は別のiPhoneデバイスで再現できるため、この特定のデバイスの問題ではありません。 (異なるiOSバージョンについてはよくわかりません)。

エラーを再現する方法:

  1. ゲームを開きます(Safari内)。
  2. 3〜4分間実行します。
  3. 通知センターを下にスライドすると、すべてがおかしくなります。
    エラーを再現する方法を示す YouTubeビデオ を追加しました(iPhone 5Cで)。
    最初に通知センターに問題が表示されるようです(メニューを上から下にスワイプした場合)。
    今のところ、この問題はiPhone 5C iOS 9.2.1(13D15)。また、新しいiOS 9.3バージョンでも発生します。

この問題をfixするには、

  1. (ゲームタブが開いている)Safariアプリケーションを閉じます。
  2. 電話をロックします。ロックを解除すると、すべてが正常に戻ります。

game自体に関する詳細:

  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

[〜#〜] demo [〜#〜]

ヤイ、バグを再現するjsFiddleを作成できました。 https://jsfiddle.net/cte55cz7/14/ iPhone 5CのSafariで開いて、数分待ちます。 iPhone 6およびiPad miniでは、ページがクラッシュします(メモリリークが原因で予想どおり)

実行したくない人のためのHasteBinの同じコードです。

79
Cristy

このメモリリークは、おそらく「WebKitのJSエンジン」がどのように機能するかによるものです[safari webkit-javascript llvm]

そして、残りのRAM(共有され、ユーザーインターフェイスのグラフィカル要素を格納するためにiOSでも使用される)に直接的な影響を与える、仮想メモリバッファオーバーフローのように見えます)

コードに比べると、「[...] jQueryのメモリリークを見つけるのは簡単です。$。cacheのサイズを確認してください。大きすぎる場合は、それを調べて、残っているエントリとその理由を確認してください。[...]」( http://javascript.info/tutorial/memory-leaks

これに関連することを期待させてくださいfor loop

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()そしてsetTimeoutに$(elem).remove()を入れます。

1
A STEFANI