V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qdwang
V2EX  ›  Apple

苹果现在 bug 越来越多的原因找到了

  •  
  •   qdwang · 16 小时 14 分钟前 · 7268 次点击

    这里大家都是程序员,只要做完这个题,就知道为什么苹果会有那么多 bug 了

    // 这是一段 swift 代码
    
    var hello = [10, 20, 30, 40, 50, 60]
    var foo = hello[2..<5] // 表示索引范围是 2 ,3 ,4
    foo[2] = 0
    print(foo) 
    
    // 先看看你的程序员直觉,输出结果是什么
    
    

    然后随便 google 找个 swift playground 运行这段代码,你就知道为什么苹果现在 bug 越来越多了。

    77 条回复    2025-10-27 15:07:37 +08:00
    ourstars
        1
    ourstars  
       15 小时 53 分钟前   ❤️ 10
    直觉理解是[30,40,0],但是运行之后是[0,40,50]。
    foo 用的是 hello 的索引范围造成了[0,40,50]的结果。
    qdwang
        2
    qdwang  
    OP
       14 小时 57 分钟前 via iPhone
    @ourstars 对,swift 设计了这个反程序员直觉的语义。导致你去问 ai ,也有一大堆 ai 会回答错误。
    TianDogK48
        3
    TianDogK48  
       14 小时 28 分钟前 via iPhone   ❤️ 1
    目测这个语法不常用,所以问题应该不大; go 里面 slice 扩容,也有坑,但是目测 go 的 slice 很少用,都是直接 make
    raycool
        4
    raycool  
       14 小时 26 分钟前
    看来都是 slice 的不同理解导致的。
    smlcgx
        5
    smlcgx  
       14 小时 23 分钟前 via iPhone
    这样看起来 swift 更偏向自然语言一点,几就是几
    butanediol2d
        6
    butanediol2d  
       13 小时 45 分钟前
    hello 是 Array<Int>
    foo 是 ArraySlice<Int>
    craftsmanship
        7
    craftsmanship  
       13 小时 32 分钟前 via Android
    如此说来 JS 早先那些垃圾设计可能导致的 bug 只会更多。。
    xuejianxianzun
        8
    xuejianxianzun  
       11 小时 54 分钟前
    @craftsmanship 怎么又扯到 js 了,js 在这个场景里没有反直觉的行为。
    hash
        9
    hash  
       8 小时 13 分钟前   ❤️ 1
    系统层核心实现是一坨屎,与语言设计无关
    Building
        10
    Building  
       7 小时 14 分钟前
    都按直觉那不同语言反人类的语法简直不要太多,当然不影响 Swift String 的设计才是最为逆天的东西
    cpstar
        11
    cpstar  
       6 小时 35 分钟前
    @xuejianxianzun 8# 以我不动 swift ,和 1#的情况,我觉得基本上就是 js 的逻辑:
    foo_js[2]=hello[2]/*30*/,foo_js[3]=hello[3]/*40*/,foo_js[4]=hello[4]/*50*/

    foo_js[0]=undefined,foo_js[1]=undefined
    于是
    foo_js[2]=0 之后,foo_js={"2":0,"3":40,"4":50}
    Greendays
        12
    Greendays  
       6 小时 31 分钟前   ❤️ 1
    和语言关系不大的,既然做了开发掌握这个语言的语法是基本要求。Bug 多只是需求超过了能力,不得不赶工和减少测试流程造成的。
    Lin0936
        13
    Lin0936  
       6 小时 23 分钟前
    再逆天的语言也该经过测试啊
    june4
        14
    june4  
       6 小时 20 分钟前
    @craftsmanship js 真没有啥绕不过去的垃圾设计,特别是 ts 上身后,底子非常干净纯洁,现在只有一个日常使用我觉得不爽,就是搞出二个 null/undefined 类型。
    xiangyuecn
        15
    xiangyuecn  
       6 小时 12 分钟前
    苹果拉的屎就是香,没啥毛病 能咽下去的
    freeloop1
        16
    freeloop1  
       6 小时 1 分钟前
    那个 foreach 的语法我是最蚌埠住的,我总感觉是少写了括号啥的,太难受了。
    a33291
        17
    a33291  
       5 小时 56 分钟前
    foo 是 hello 的视图吧? 现在很多语言/库都提供这种东西,减少 copy,不过大部分是只读的比较合理
    cookii
        18
    cookii  
       5 小时 43 分钟前 via Android
    @a33291 视图通常只读会好一点
    fadaixiaohai
        19
    fadaixiaohai  
       5 小时 37 分钟前
    从 swift6 开始越来越复杂,关键词越搞越多,问题是有些关键词编译器都支持不太好,搞不好就容易 crash ,对新人也不太友好
    PlG5sBkXD1ziLeGB
        20
    PlG5sBkXD1ziLeGB  
       5 小时 33 分钟前 via iPhone
    swift 的语法真是丑到爆,OC 的语法虽然跟裹脚布一样长,但是可读性非常强
    deplives
        21
    deplives  
       5 小时 30 分钟前
    kfpenn
        22
    kfpenn  
       5 小时 28 分钟前
    liuhan907
        23
    liuhan907  
       5 小时 15 分钟前   ❤️ 1
    这个输出 [0, 40, 50] 不才是符合直觉的?
    cpper
        24
    cpper  
       5 小时 12 分钟前
    每个语言有每个语言的用法,不要陷入奇巧淫技的细节中
    liuhan907
        25
    liuhan907  
       5 小时 7 分钟前   ❤️ 1
    哦,大意了,foo[2] 啊。那这是傻逼设计,我看的不够仔细啊 -_-
    IDAEngine
        26
    IDAEngine  
       5 小时 5 分钟前
    这是一个关于 Swift 数组切片( Array Slice )行为的有趣问题。

    我的“程序员直觉”告诉我,输出结果是:

    [0, 40, 50]
    解释(为什么我的直觉是这样):

    切片操作:

    var hello = [10, 20, 30, 40, 50, 60]

    var foo = hello[2..<5] 创建了一个数组切片( Array Slice )。这个切片包含 hello 中索引 2,3,4 的元素。

    foo 此时是 [30, 40, 50]。

    关键点:数组切片的索引继承了原数组的索引。所以 foo 内部元素的索引是 2,3,4 ,而不是从 0 开始。

    修改切片:

    foo[2] = 0 尝试修改 foo 中索引为 2 的元素。

    在 foo 中,索引 2 对应的值是 30 。

    修改后,foo 变成了 [0, 40, 50](注意,尽管内部值变了,它的索引结构仍然是 2,3,4 )。

    打印切片:

    print(foo) 打印切片的内容。Swift 在打印数组切片时,会打印其包含的元素值。

    因此,输出是 [0, 40, 50]。
    november
        27
    november  
       5 小时 2 分钟前 via iPhone   ❤️ 1
    @cpstar 主楼的代码应该是对标的 js 的 Array.slice(2,5)
    ccnoobs
        28
    ccnoobs  
       5 小时 0 分钟前
    @IDAEngine "在 foo 中,索引 2 对应的值是 30 。" 不知道你怎么想的
    yuanxing008
        29
    yuanxing008  
       4 小时 57 分钟前
    可能 go 和 python 写的太久了 我的第一直觉就是[0,40,50]

    后面看你们讨论才发现这个设计很扯,但是写习惯了就好了😁
    2en
        30
    2en  
       4 小时 55 分钟前
    居然不是 30,40,0
    leeg810312
        31
    leeg810312  
       4 小时 51 分钟前
    @IDAEngine 你的直觉才有问题。无论是创建新数组,还是视图,都不该是这样的行为。按业务逻辑,这段代码描述应该是这样,从 A 数组中取第 2-4 (从 0 开始)的元素,作为 B 数组,B 数组的第 2 个元素值改为 0 。关键点,B 的第 2 个元素,实际行为却去改 A 的第 2 个,这完全是反直觉的,即使 B 是视图,非实际创建的新数组,也应当把 B 当作一个逻辑上独立的数组去使用。
    PlG5sBkXD1ziLeGB
        32
    PlG5sBkXD1ziLeGB  
       4 小时 49 分钟前 via iPhone
    @yuanxing008 啊?你用过 go 的 slice 吗兄弟,还是你把 `foo[2] = 0` 看成 `hello[2] = 0`了
    cpstar
        33
    cpstar  
       4 小时 47 分钟前
    @november 27# 如果是 js ,那会生成一个新的副本,foo_js 是{"0":30,"1":40,"2":50},然而按照 21#,更像是 foo 就是 hello ,修改了 foo 一并修改 hello ,只不过限定了 foo 的游标只有 2 、3 、4 取值,如果 foo[1]是不是要报错。
    ripperdev
        34
    ripperdev  
       4 小时 46 分钟前
    @yuanxing008 go 不是这样啊,输出肯定是[30 40 0]
    superrichman
        35
    superrichman  
       4 小时 45 分钟前   ❤️ 1
    @yuanxing008 #28 你这 python 没学好啊 🐶 python 里面是 [30, 40, 0]

    一行命令跑出来就知道了
    python -c "a=[10,20,30,40,50,60];b=a[2:5];b[2]=0;print(b)"
    guanhui07
        36
    guanhui07  
       4 小时 44 分钟前
    之前我也学了下 swift 的语法..
    leeg810312
        37
    leeg810312  
       4 小时 43 分钟前
    @yuanxing008 你的直觉错了。go 我不知道,python 的切片是创建一个新 list
    a = [0, 2, 4, 6, 8, 10]
    b = a[2:5]
    b[0] = 3
    b[2] = 9
    print(a)
    print(b)
    输出:
    [0, 2, 4, 6, 8, 10]
    [3, 6, 9]
    wangkun2012324
        38
    wangkun2012324  
       4 小时 41 分钟前   ❤️ 1
    正确的用法永远是使用 startIndex 和 offset/advanced
    ```
    var hello = [10, 20, 30, 40, 50, 60]
    var foo = hello[2..<5] // 表示索引范围是 2 ,3 ,4
    foo[foo.startIndex.advanced(by: 2)] = 0
    print(foo) // [30, 40, 0]

    ```
    junj2121
        39
    junj2121  
       4 小时 40 分钟前
    C 出生的人 一看就知道结果啊。
    这种赋值逻辑肯定是从指针赋值上考虑。
    都觉得 C 垃圾,其实并不然
    noErr
        40
    noErr  
       4 小时 39 分钟前
    js 仔采用 switft
    Torpedo
        41
    Torpedo  
       4 小时 36 分钟前
    @june4 #14 这两个可以不分的。主流库里,常见的只有 react 组件的返回和 json 严格区分了这两个。js 判断 null 和 undefined 如果不分,直接用 a!=null 判断就行了
    idonttellyou
        42
    idonttellyou  
       4 小时 32 分钟前
    为什么不是[30, 40, 0]呢...
    V2Try
        43
    V2Try  
       4 小时 29 分钟前 via iPhone
    这个确实奇怪,如果
    foo[2] = 0
    换成
    hello[2] = 0

    结果是不是一样的?
    Wanex
        44
    Wanex  
       4 小时 28 分钟前
    先别管 swift 符不符合直觉,也别管 js 有没有同样的问题,总之先骂 js 再说🤣
    november
        45
    november  
       4 小时 26 分钟前 via iPhone
    @cpstar 其实 #27 的答案是 ai 结果,实际运行结果是 hello 没变。
    所以更验证了楼主说的“一堆 ai 回答错”。233333
    ChrisFreeMan
        46
    ChrisFreeMan  
       4 小时 26 分钟前
    @idonttellyou 因为很多人理所当然的认为 foo 是拷贝了一个新的数组,实际上 foo[2]指向了原来的数组,foo 只是引用了愿数组 hello 。
    ufan0
        47
    ufan0  
       4 小时 25 分钟前
    @junj2121 以此举例不大合适吧,C 有明确的指针概念,甚至初学者都会接触到。
    PlG5sBkXD1ziLeGB
        48
    PlG5sBkXD1ziLeGB  
       4 小时 21 分钟前
    @ChrisFreeMan #46 按照你的说法,foo[2]指向原来的数组,那修改 foo[2]后原来的数组是不是应该改变呢
    Huelse
        49
    Huelse  
       4 小时 21 分钟前
    确实反直觉
    beimenjun
        50
    beimenjun  
    PRO
       4 小时 21 分钟前
    这个并不是苹果现在 bug 越来越多的原因。

    我还以为是讲技术债之类的,结果居然是吐槽 Swift 的语法。真的是浪费时间。
    ChrisFreeMan
        51
    ChrisFreeMan  
       4 小时 20 分钟前
    @idonttellyou 好吧,我搞错了,自己试了下,原数组并没有改变值
    ChrisFreeMan
        52
    ChrisFreeMan  
       4 小时 20 分钟前
    @PlG5sBkXD1ziLeGB 我刚刚试了下确实是,没有改变,我收回我的傻逼言论
    lihanst
        53
    lihanst  
       4 小时 18 分钟前
    看到这种结果,应该思考为什么要设计一个 ArraySlice ?目的是什么?代价是什么?
    VXF2016
        54
    VXF2016  
       4 小时 18 分钟前
    真离谱
    caiqichang
        55
    caiqichang  
       4 小时 18 分钟前
    确实反直觉
    usVexMownCzar
        56
    usVexMownCzar  
       4 小时 18 分钟前 via iPhone
    没啥好吵的,swift 的 Array 并不是传统概念的数组,真正的数组(连续存储)是最近版本才添加的,InlineArray 。

    话说目前还没看到从源代码来分析上面的代码为什么这么设计🌚
    mizuki9
        57
    mizuki9  
       4 小时 7 分钟前
    好奇葩的设计,好奇葩的行为
    luodan
        58
    luodan  
       4 小时 3 分钟前
    学习了。以后遇到 slice 多留意点。
    FlashEcho
        59
    FlashEcho  
       4 小时 1 分钟前
    @junj2121 #39 c 根本就没有数组切片,得自己手写,天然就会知道这个是一个视图还是拷贝出来一个新的,不参与讨论。c++的数组拷贝是默认拷贝出来一个新的
    whitefable
        60
    whitefable  
       3 小时 53 分钟前
    @junj2121 #39 即使是 C 出身也很反直觉好吧。 要说从指针赋值上去考虑,那 foo 不应该也是指向了 hello[2]么,那此时 foo[2]也是对应 hello[4]才对
    beimenjun
        61
    beimenjun  
    PRO
       3 小时 30 分钟前
    @usVexMownCzar

    苹果关于 ArraySlice 的文档( https://developer.apple.com/documentation/swift/arrayslice )原话是这么说的“Sharing indices between collections and their subsequences is an important part of the design of Swift’s collection algorithms.”

    除了 Array 和 ArraySlice ,甚至 String 和 Substring ,Data 和 Data.SubSequence 都是这样子的。

    感觉最直观的好处就是可以在原对象和切片之间需要频繁调整 index 的步骤简化掉这一步骤。

    比如 Apple 的示例:

    ```
    let absences = [0, 2, 0, 4, 0, 3, 1, 0]

    if let i = absences.firstIndex(where: { $0 > 0 }) { // 1
    let absencesAfterFirst = absences[(i + 1)...] // 2
    if let j = absencesAfterFirst.firstIndex(where: { $0 > 0 }) { // 3
    print("The first day with absences had \(absences[i]).") // 4
    print("The second day with absences had \(absences[j]).")
    }
    }

    // Prints "The first day with absences had 2."
    // Prints "The second day with absences had 4."
    ```
    xz410236056
        62
    xz410236056  
       3 小时 30 分钟前
    @ourstars
    @qdwang
    因为你们没看文档,foo 并不是个 array ,而是一个切片(ArraySlice),他的索引并不从 0 开始(取决于你创建方式,大多数共享)。你需要使用特定的函数达到 array 的效果。

    https://developer.apple.com/documentation/swift/arrayslice
    xz410236056
        63
    xz410236056  
       3 小时 30 分钟前
    @fadaixiaohai 关键词现在能搞本书了,恶心的要死。人类根本记不全
    Leeeeex
        64
    Leeeeex  
    PRO
       3 小时 16 分钟前
    @craftsmanship
    也不管原因是啥,也不实践试试 js 有没有同样的问题,反正碰到奇葩问题直接喷 js 就是 zzzq ?
    ikw
        65
    ikw  
       2 小时 52 分钟前
    @PlG5sBkXD1ziLeGB #48 前面保留 index 我都能认为是语言的特性,找到对应的文档并且接受了,但是不修改原数组我就懵了。

    Slice 作为保留原 index 的轻量化视图,修改操作不影响原数组,那修改部分去哪里了?总不能是 Copy on Write 新建了一个 Array 吧,而且这么奇怪的逻辑没有文档强调?

    https://developer.apple.com/documentation/swift/arrayslice
    qdwang
        66
    qdwang  
    OP
       2 小时 44 分钟前 via iPhone
    @wangkun2012324 老哥厉害👍
    coudey
        67
    coudey  
       2 小时 41 分钟前
    有够奇葩的,普通 gpt5 也搞错了。hello 被修改能理解,foo[2]用 hello 的索引真是逆大天
    sankemao
        68
    sankemao  
       2 小时 20 分钟前
    太奇葩了,就你 swift 要搞特殊
    butanediol2d
        69
    butanediol2d  
       2 小时 7 分钟前
    @PlG5sBkXD1ziLeGB COW 生效了,在修改之前,底层使用的 array 是同一块内存,修改时 foo 进行了复制,不是同一块内存了
    yolee599
        70
    yolee599  
       1 小时 29 分钟前 via Android
    @junj2121 #39 错误的,C 语言没有这种反直觉的设计:
    https://onecompiler.com/c/442uqznbg
    wangkun2012324
        71
    wangkun2012324  
       1 小时 17 分钟前
    swift 的数组,包括之前吐槽的 string, 一切源于 collection 的设计,collection 的 startIndex 和 endIndex, swift 一致期望用户不使用整数索引,你查看数组的一些排序算法等你会发现根本不是数组的方法,而是 collection 甚至别的 sequence 的各种方法,并且不是所有。
    设计者在做通用的 collection 算法时,https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/14 决定了 Array Slice 这么做。其实我认为这么做之后,array[1]这种风格的写法, 只是 swift 数组只是恰好对的写法,因为 Array.Index 是 Int 。string[1]就不对了。吐槽也是合理的。毕竟是常见的语法,违反直觉的语义
    Azone
        72
    Azone  
       1 小时 15 分钟前
    @PlG5sBkXD1ziLeGB foo[2] 指向的是元素组的索引,不是指向的是原数组。Swift 所有的值类型都是 COW 技术,当你把一个变量赋值给另一个变量并且改变它的值的时候,他们指向的就不是同一块内存了。
    laikicka
        73
    laikicka  
       1 小时 7 分钟前
    @craftsmanship js 的 bug 还少吗
    decken
        74
    decken  
       54 分钟前
    语法问题导致的 bug 应该极少吧
    clarkethan
        75
    clarkethan  
       25 分钟前
    想了一下,有可能是出于性能考虑吧,减少不必要的深拷贝和内存分配

    再深入想一想,如果我是经常使用 swift 的人,这个设计好像又是可以接受的,语法上这种写法就相当于是一种借用切片,语以上只是记录一下借用范围而已。如果需要在 var foo 的时候直接深拷贝内容,直接用 Array 构造一个新的数据,应该是语言推荐的做法吧。如果有以上心智在前,好像倒不会有太多问题了,不少场合确实能节约一些性能,尤其是资源能耗敏感的场景

    我实际上不写 swift ,仅猜测
    ikw
        76
    ikw  
       13 分钟前
    @butanediol2d #69 这样的话,又有几个问题,
    既然设计了 CoW ,那 foo 预期是会作为独立对象存在的,为什么还要用原数组的索引呢?

    CoW 是复制整个 Array 还是复制 slice 对应部分呢?
    foo 有了自己的内存,按说讲他和 hello 已经没什么关系了,这个时候 foo 还要一直用 hello 的索引,似乎也很奇怪吧?
    tiancaixiaoshuai
        77
    tiancaixiaoshuai  
       4 分钟前
    直觉是重置索引,我没学过 swift ,如果这是语言特性,也能接受,就像 php 也可以使用 array_slice()里面的 preserve_keys 设为 true 保留原索引,并且这个跟 bug 多也没什么关系吧
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   5444 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:12 · PVG 15:12 · LAX 00:12 · JFK 03:12
    ♥ Do have faith in what you're doing.