배열 상태는 iOS 12 Safari에서 캐시됩니다. 버그입니까 아니면 기능입니까?


432

2018.10.31에 업데이트

이 버그는 iOS 12.1에서 수정되었습니다. 좋은 하루 되세요 ~

새로 출시 된 iOS 12 Safari에서 Array의 값 상태에 문제가 있음을 발견했습니다 (예 : 다음과 같은 코드).

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>

페이지를 새로 고친 후에도 여전히 배열 값이 반대로 바뀝니다. 이것은 새로운 Safari의 버그입니까?


여기 데모 페이지가 있습니다. iOS 12 Safari에서 사용하십시오 : https://abelyao.github.io/others/ios12-safari-bug.html


41
- 버그는 맥 OS 10.14 모하비도 확인 i.imgur.com/ZJtJJC1.png
a_rahmanshah

43
Safari 버전 12.0 (13606.2.11)이 설치된 macOS 10.13.6 (High Sierra)에는 동일한 문제가 있습니다. 페이지를 새로 고친 후에도 배열이 계속 반전됩니다.
Kevin Gimbel

2
iOS 12.1뿐만 아니라 Safari 12.0.1 (macOS)에서도 버그가 수정되었습니다.
MrMister

답변:


272

확실히 버그입니다! 그리고 그것은 매우 심각한 버그입니다.

버그는 모든 값이 기본 리터럴 인 배열 이니셜 라이저의 최적화 때문입니다. 예를 들어 다음과 같은 기능이 주어집니다.

function buildArray() {
    return [1, null, 'x'];
}

호출에서 반환 된 모든 배열 참조 buildArray()는 동일한 메모리에 연결되며 toString()결과 와 같은 일부 메서드 는 캐시됩니다. 일반적으로 일관성을 유지하기 위해 최적화 된 어레이에서 변경 가능한 조작은 데이터를 별도의 메모리 공간에 복사하여 링크합니다. 이 패턴이라고 기록 중 복사 짧게, 또는 소.

reverse()메소드는 배열을 변경하므로 기록 중 복사를 트리거해야합니다. 그러나 원래의 구현 자 (Apple의 Keith Miller)는 reverse()많은 테스트 사례를 작성했지만 사례를 놓 쳤기 때문에 그렇지 않습니다 .

이 버그는 8 월 21 일 Apple에보고되었습니다 .이 수정 프로그램 8 월 27 일 WebKit 저장소 에 제공되어 2018 년 10 월 30 일 Safari 12.0.1 및 iOS 12.1에 제공되었습니다.


11
참고 : Mac OS X의 Safari 12.0에도 동일한 문제가 있습니다.
hax September

17
예, 이미 소스에서 수정되었고 Safari Technology Preview로 이미 제공되었습니다. Safari Technology Preview 65에서 cdn.miss.cat/demo/ios12-safari-bug.html 을 사용해보십시오 . 버그가없는 것을 알 수 있습니다.
사이드 쇼바 커

6
버그의 근본 원인은 인덱스 믹스 업의 결과라고 생각하지 않습니다. 대신, 수정하기 전에 객체가 불변인지 여부를 확인하지 않으면 발생하는 것으로 보입니다. 슬라이스 문제는 비슷한 설명이있을 수 있지만 동일하지는 않지만 패치로 수정되지는 않습니다. 슬라이스 문제에 대한 WebKit 버그 보고서를 여는 것을 고려해야합니다.
Zenexer 2014 년

5
@Zenexer 당신이 맞아요. bugs.webkit.org/show_bug.cgi?id=188794를 발견 하고 소스 코드를보기 전에이 답변을 작성했습니다 . 답변을 편집하겠습니다.
hax

75

버그를 해결하기 위해 lib를 작성했습니다. https://www.npmjs.com/package/array-reverse-polyfill

이것은 코드입니다 :

(function() {
  function buggy() {
    var a = [1, 2];
    return String(a) === String(a.reverse());
  }
  if(!buggy()) return;
  var r = Array.prototype.reverse;
  Array.prototype.reverse = function reverse() {
    if (Array.isArray(this)) this.length = this.length;
    return r.call(this);
  }
})();


4
언제든지 업데이트하십시오. 기여에 오신 것을 환영합니다.
Edire Fan

14
@zephi, length ( this.length = this.length)에 쓰면 Copy On Write가 트리거되므로 배열의 메모리 주소가 변경되어의 동작이 수정됩니다 reverse.
Cœur

14

이것은 웹킷 의 버그입니다 . 이것은 최종적으로 해결되었지만 아직 iOS GM 릴리스와 함께 제공되지는 않았습니다. 이 문제에 대한 해결책 중 하나 :

(function() {
  function getReverseStr() {
    return [1, 2].reverse();
  }

  var n1 = getReverseStr()[0];
  var n2 = getReverseStr()[0];
  // check if there is an issue
  if(n1 != n2) {
    var origReverseFunction = Array.prototype.reverse;
    Array.prototype.reverse = function() {
      var newArr = this.slice();
      // use original reverse function so that edge cases are taken care of
      origReverseFunction.apply(newArr, arguments);
      var that = this;
      // copy reversed array
      newArr.forEach(function(value, index) {
        that[index] = value;
      });
      return this;
    }
  }
})();

6

요소 수가 변경되면 캐시되지 않는 것 같습니다.
나는 이것을 이렇게 피할 수 있었다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        arr.push('');
        arr.pop();
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.