正在开发的程序中,有一段代码大概如下:
window.addEventListener("load", function ()
{
var arr = [1, 2, 3, 4];
arr.reverse();
});
今天升级了 iOS 12 之后,无意中发现数组的顺序有问题。
第一次加载是正常的, 页面刷新会颠倒, 再刷新又正常了, 再刷新又颠倒了, 以此类推…
而如果是关闭了页面、重新打开,则不会有问题。
在 PC Chrome / Android / iOS 11 Safari 中都没出现这个问题,就好像这个变量,被 safari “缓存” 了一样,即使刷新页面也会被保留、继承之前的状态。
这是 iOS 12 Safari 的 bug 还是 feature 啊?有没有哪里可以看到关于 safari 底层机制的改变呢?
1
uuair 2018-09-18 21:18:35 +08:00
adblock 没法用了。。
|
2
34C OP @uuair 求帮顶,如果这个 “ feature ” 真的存在,那依赖 js 变量做状态的(比如 var userinfo 之类的),要出很多大坑了
|
3
34C OP 经测试,改成 var arr = new Array(1, 2, 3, 4); 之后则没问题,稍后 append 到主题中。
看样子如果不是明确 new 的话是会被缓存了…… |
5
34C OP @gkiwi 想象一下有多少人升级 12 而不会升级 12.1 的,如果是 bug 以后即便修复了也会有人一直受影响,如果是 feature 这特么也太严重了,多少项目多少代码会受影响,还有 npm 上那些被数千万项目引用的包…
|
6
gkiwi 2018-09-18 22:06:37 +08:00
@34C #5 定性肯定是 bug。我这边测试不加 load,直接 alert 出来数据都偶发不对。
一方面浏览器修,二方面就是靠 pollify 了 |
7
34C OP @gkiwi 我这边测试是 load 事件都会触发,但 load 中申明的变量,有概率会被缓存,太坑了,还好项目开发了一半,有改语法的机会
|
8
yinanc 2018-09-18 22:22:41 +08:00 via iPhone
mark
前端日常骂娘时间 |
11
34C OP @yinanc 所以我的标题是问有哪些变化啊… 因为我这是小项目,也没提前升级测试版 iOS 12,所以来问问看苹果之前有没有公开说明过 safari 会有那些改动啊,除了苹果官方的说明是一种 “确认” 我不知道还能怎么自己先确认了,我和楼上的都测试过可以复现问题,难道是我对 js 的工作机制理解有误… 那 ios 11 和 chrome 之类的都没问题啊…
|
12
edire 2018-09-19 02:53:40 +08:00
写了个文件修复这个问题:
https://www.npmjs.com/package/fix-array-reverse https://github.com/fanmingfei/array-reverse-ios12 目前发现只有使用 reverse 才会缓存,另外,不知道苹果是否会热更新修复这个问题。 |
13
edire 2018-09-19 04:44:27 +08:00
npm 现在限制实在太多了。本来想 npm unpublish 一下再 publish,告诉我 24 小时之内不能 publish 索性改了个名字 https://www.npmjs.com/package/array-reverse-polyfill
|
14
Trim21 2018-09-19 05:30:35 +08:00 via Android
成功复现…
|
15
hax 2018-09-19 06:26:41 +08:00 6
这显然是一个 bug。并且是个惊天大 bug。
此 bug 跟 load 事件无关。直接执行即可重现。与刷新也并不直接相关。 简化的测试代码见: https://github.com/hax/hax.github.com/blob/master/browser-bugs/ios12-safari-array-reverse/test.html 测试页面链接: https://johnhax.net/browser-bugs/ios12-safari-array-reverse/test 此 bug 的本质是,Safari 对所有值是 primitive literal (如 null, true, 1, 'x' 是,但 /x/,undefined、NaN 就不是)的 array initializer 做了优化,同一个 initializer 产生的数组在内存里永远指向一份,其 toString 的结果也预先计算好,所以 reverse()之后 toString()结果不变,但实际数组已经变化。正常来说,如果该 array 执行了任何修改操作,则复制到一份独立内存去。这是所谓 copy-on-write 的优化策略。但不幸的,reverse 方法没有触发 CoW。 另一方面,所有不修改 array 的方法应该不触发 CoW。我实测下来,甚至 copyWithin 和 fill 这样的方法,如果 start/end 相同使得实际上并没有修改效果,也不会触发 CoW。但是神奇的是 slice()会触发 CoW。所以我猜有可能某个苹果的临时工把 reverse/slice 的方法索引搞颠倒了。 |
18
34D 2018-09-19 13:41:13 +08:00
没我大。
|
20
oh 2018-09-19 16:42:00 +08:00
@hax 你这个分析并不能解释为什么 safari 为了性能要缓存变量啊,页面都 reload 了内存都不清空,为了性能值得这么冒险吗?那说不定还有其它隐藏 bug 呢
|
21
JayZangwill 2018-09-19 16:49:29 +08:00
这个问题在 qq 浏览器上也能复现
|
22
34C OP @JayZangwill ios 上所有浏览器都是基于 safari 内核
|
25
mrcode 2018-09-19 19:43:36 +08:00
这个问题是因为什么导致的呢?
|
26
34C OP |
27
hax 2018-09-19 21:11:59 +08:00
@oh 根据源码来看,基本上就是我说的问题(除了 reverse/slice 颠倒的瞎猜之外)。至于说刷新,其实是无关的。估计应该是 safari 在刷新本页的时候,一看啥都没变,就不销毁之前的资源继续用了。实际上不是每次刷新都复用的,有概率全清掉的。
|
28
hax 2018-09-19 21:15:04 +08:00
bug 来源: https://github.com/WebKit/webkit/commit/c02f5d334455d7fe8b16fe642d1f5900c5cde6e9
修复: https://bugs.webkit.org/show_bug.cgi?id=188794 是的,上个月 webkit 已经修掉了( 6 月上的 bug 代码),但是 apple 不知道为啥这次发版没把 patch 打上。 |
29
hax 2018-09-19 21:17:58 +08:00
至于为什么要用 CoW,当然就是为了性能啰。在 https://bugs.webkit.org/show_bug.cgi?id=185003 这里有提到在某些性能测试中有显著提升。
但是 CoW 很复杂,一下改了好多代码,所以出 bug 了。其实当时作者也写了很多 testcase,无奈还是漏掉了 reverse()。 |
30
mrcode 2018-09-19 23:00:38 +08:00
大胆猜测一下 hax 就是那个实习生 (逃
|
31
spiderGgl 2018-09-25 23:23:07 +08:00
兄弟,你吃屎了,这样害我
|