สถานะอาร์เรย์จะถูกแคชใน iOS 12 Safari มันเป็นข้อบกพร่องหรือคุณสมบัติ?


432

อัปเดตที่ 2018.10.31

ข้อผิดพลาดนี้ได้รับการแก้ไขใน iOS 12.1 มีวันที่ดี ~

ฉันพบปัญหาเกี่ยวกับสถานะค่าของ Array ใน Safari Safari iOS 12 ที่เพิ่งเปิดตัวตัวอย่างเช่นโค้ดดังนี้:

<!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
ข้อผิดพลาดได้รับการยืนยันใน macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah

43
macOS 10.13.6 (High Sierra) พร้อม Safari เวอร์ชัน 12.0 (13606.2.11) มีปัญหาเดียวกัน อาร์เรย์ยังคงกลับด้านหลังจากรีเฟรชหน้า
Kevin Gimbel

2
ข้อผิดพลาดได้รับการแก้ไขใน Safari 12.0.1 (macOS) เช่นเดียวกับใน iOS 12.1
MrMister

คำตอบ:


272

เป็น BUG แน่นอน! และมันเป็นข้อผิดพลาดที่ร้ายแรงมาก

ข้อผิดพลาดเกิดจากการเพิ่มประสิทธิภาพของอาร์เรย์ initializers ซึ่งค่าทั้งหมดเป็นตัวอักษรดั้งเดิม ตัวอย่างเช่นกำหนดฟังก์ชั่น:

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

การอ้างอิงอาร์เรย์ที่ส่งคืนทั้งหมดจากการเรียกไปยังbuildArray()จะเชื่อมโยงไปยังหน่วยความจำเดียวกันและวิธีการบางอย่างเช่นtoString()จะมีการแคชผลลัพธ์ โดยปกติเพื่อรักษาความสอดคล้องการดำเนินการที่ไม่แน่นอนในอาร์เรย์ที่เหมาะสมดังกล่าวจะคัดลอกข้อมูลไปยังพื้นที่หน่วยความจำแยกต่างหากและเชื่อมโยงไปยังมัน รูปแบบนี้เรียกว่าcopy-on-writeหรือ CoW แบบย่อ

reverse()วิธีการแปรรูปอาร์เรย์ดังนั้นจึงควรเรียกเขียนคัดลอกเมื่อ แต่มันไม่ได้เป็นเพราะผู้ดำเนินการดั้งเดิม (Keith Miller ของ Apple) พลาดreverse()กรณีแม้ว่าเขาจะเขียนบทความทดสอบมากมาย

ข้อผิดพลาดนี้ถูกรายงานไปยัง Appleในวันที่ 21 สิงหาคมการแก้ไขที่ดินในที่เก็บ WebKitเมื่อวันที่ 27 สิงหาคมและจัดส่งใน Safari 12.0.1 และ iOS 12.1 ในวันที่ 30 ตุลาคม 2018


11
หมายเหตุ: Safari 12.0 บน Mac OS X ยังมีปัญหาเดียวกัน
hax

17
ใช่มันได้รับการแก้ไขในแหล่งที่มาแล้วและจัดส่งใน Safari Technology Preview แล้ว ลองcdn.miss.cat/demo/ios12-safari-bug.htmlใน Safari Technology Preview 65 คุณจะพบว่าไม่มีข้อผิดพลาด
sideshowbarker

6
ฉันไม่เชื่อว่าสาเหตุของข้อผิดพลาดนั้นเป็นผลมาจากการรวมดัชนี แต่ดูเหมือนว่าจะเกิดจากการละเลยที่จะตรวจสอบว่าวัตถุนั้นไม่เปลี่ยนรูปก่อนที่จะแก้ไขหรือไม่ ปัญหาชิ้นอาจมีคำอธิบายที่คล้ายกัน แต่มันไม่เหมือนกัน แต่และจะไม่ได้รับการแก้ไขโดยโปรแกรมแก้ไขสำหรับการย้อนกลับเท่าที่ฉันสามารถบอกได้ คุณควรพิจารณาเปิดรายงานบั๊ก WebKit สำหรับปัญหาการแบ่ง
Zenexer

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

14
@zephi ผมคิดว่าการเขียนกับความยาวที่ ( this.length = this.length) จะเรียก Copy reverseในการเขียนดังนั้นจะมีการเปลี่ยนแปลงที่อยู่ในหน่วยความจำของอาร์เรย์และเพื่อจะแก้ไขพฤติกรรมของ
Cœur

14

นี่คือข้อผิดพลาดในWebKit แม้ว่านี่จะได้รับการแก้ไขในตอนท้าย แต่ยังไม่ได้จัดส่งพร้อมกับการเปิดตัว 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.