Trạng thái mảng sẽ được lưu trong bộ nhớ cache iOS 12. Đây có phải là một lỗi hoặc tính năng?


432

Cập nhật vào 2018.10.31

Lỗi này đã được sửa trong iOS 12.1, chúc một ngày tốt lành ~

Tôi đã tìm thấy sự cố với trạng thái giá trị của Array trong Safari 12 iOS mới phát hành, ví dụ, mã như thế này:

<!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>

Sau khi làm mới trang, giá trị của mảng vẫn bị đảo ngược. Đây có phải là một lỗi hoặc một tính năng của Safari mới?


Đây là một trang demo. Hãy thử sử dụng nó với Safari 12 iOS: https://abelyao.github.io/others/ios12-safari-orms.html


41
Lỗi cũng được xác nhận trong macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah

43
macOS 10.13.6 (High Sierra) với Safari Phiên bản 12.0 (13606.2.11) có cùng một vấn đề. Mảng vẫn bị đảo ngược sau khi làm mới trang.
Kevin Gimbel

2
Lỗi này đã được sửa trong Safari 12.0.1 (macOS), cũng như trong iOS 12.1.
MrMister

Câu trả lời:


272

Đó chắc chắn là một BUG! Và đó là một lỗi rất nghiêm trọng.

Lỗi này là do tối ưu hóa các bộ khởi tạo mảng trong đó tất cả các giá trị là các chữ gốc. Ví dụ, cho hàm:

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

Tất cả các tham chiếu mảng được trả về từ các lệnh gọi buildArray()sẽ liên kết đến cùng một bộ nhớ và một số phương thức như toString()sẽ lưu kết quả của chúng vào bộ nhớ cache. Thông thường, để duy trì tính nhất quán, mọi hoạt động có thể thay đổi trên các mảng được tối ưu hóa như vậy sẽ sao chép dữ liệu vào một không gian bộ nhớ riêng và liên kết với nó; mẫu này được gọi là copy-on-write hoặc CoW.

Các reverse()đột biến phương pháp mảng, vì vậy nó nên kích hoạt một copy-on-write. Nhưng nó không, bởi vì người triển khai ban đầu (Keith Miller của Apple) đã bỏ lỡreverse() vụ kiện, mặc dù ông đã viết nhiều bản thử nghiệm.

Lỗi này đã được báo cáo cho Apple vào ngày 21 tháng 8. Bản sửa lỗi đã hạ cánh trong kho lưu trữ WebKit vào ngày 27 tháng 8 và được gửi trong Safari 12.0.1 và iOS 12.1 vào ngày 30 tháng 10 năm 2018.


11
Lưu ý: Safari 12.0 trên Mac OS X cũng có vấn đề tương tự.
hax

17
Vâng, nó đã được sửa trong các nguồn và đã được gửi trong Safari Technology Preview. Hãy thử cdn.miss.cat/demo/ios12-safari-orms.html trong Safari Technology Preview 65. Bạn sẽ thấy rằng nó không có lỗi.
sIDIAbarker

6
Tôi không tin rằng nguyên nhân cơ bản của lỗi là kết quả của việc trộn chỉ mục; thay vào đó, nó dường như được gây ra bằng cách bỏ qua việc kiểm tra xem một đối tượng có bất biến trước khi sửa đổi nó hay không. Vấn đề lát cắt có thể có một lời giải thích tương tự, nhưng nó không giống nhau nhưng sẽ không được sửa chữa bởi bản vá để đảo ngược, theo như tôi có thể nói. Bạn nên xem xét việc mở báo cáo lỗi WebKit cho sự cố lát.
Zenexer

5
@Zenexer Bạn nói đúng. Tôi đã viết câu trả lời này trước khi tôi tìm thấy bug.webkit.org/show_orms.cgi?id=188794 và xem mã nguồn. Tôi sẽ chỉnh sửa câu trả lời của tôi.
hax

75

Tôi đã viết một lib để sửa lỗi. https://www.npmjs.com/package/array-reverse-polyfill

Đây là mã :

(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
Cập nhật bất cứ lúc nào. Chào mừng bạn đến đóng góp.
Người hâm mộ Edire

14
@zephi, tôi đoán rằng việc viết trên length ( this.length = this.length) sẽ kích hoạt Copy On Write, do đó sẽ thay đổi địa chỉ bộ nhớ của mảng và do đó sẽ khắc phục hành vi của reverse.
Cœur

14

Đây là một lỗi trong webkit . Mặc dù điều này đã được giải quyết vào cuối của họ nhưng chưa được phát hành với bản phát hành iOS GM. Một trong những giải pháp cho vấn đề này:

(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

Dường như không được lưu trữ nếu số lượng phần tử thay đổi.
Tôi đã có thể tránh điều này như thế này.

<!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>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.