为啥 Swift 数组插入和 removeAtIndex 操作行为不一致?

Posted

技术标签:

【中文标题】为啥 Swift 数组插入和 removeAtIndex 操作行为不一致?【英文标题】:Why do Swift array insert and removeAtIndex operations behave inconsistently?为什么 Swift 数组插入和 removeAtIndex 操作行为不一致? 【发布时间】:2014-08-02 15:35:02 【问题描述】:

我认为交换 Swift 数组的第 0 项和第 1 项是合法的,如下所示:

在索引 0 处调用 removeAtIndex,这会将第 1 项随机播放回索引 0 在索引 1 处插入已删除的项目。

但我看到的行为不一致,具体取决于我的编码方式。

代码

func test() 

    class Test 

        var array = ["foo", "bar"]

        func swap1()        // PRODUCES STRANGE RESULT
            array.insert(array.removeAtIndex(0), atIndex:1)
            print("---swap1---", xs:array)
        

        func swap2()        // PRODUCES EXPECTED RESULT
            let item = array.removeAtIndex(0)
            array.insert(item, atIndex:1)
            print("---swap2---", xs:array)
        

        func swap3()        // PRODUCES EXPECTED RESULT
            var array = ["foo", "bar"]
            array.insert(array.removeAtIndex(0), atIndex:1)
            print("---swap3---", xs:array)
        

        func print(fn: String, xs: [String]) 
            println(fn)
            for x in xs  println(x) 
        
    

    Test().swap1()
    Test().swap2()
    Test().swap3()

输出

---swap1---
foo
foo
bar
---swap2---
bar
foo
---swap3---
bar
foo

出乎意料(对我来说),swap1() 克隆了第 0 个元素而不是删除它,从上面的 swap1 输出中重复的“foo”可以看出。似乎产生这种行为的特征是

将 removeAtIndex() 的结果作为第一个参数传递给 insert() 调用,而不是将其存储在中间常量中(后者由 swap2() 完成) 对类成员变量而不是函数局部变量进行操作(后者由 swap3() 完成)。

我的问题:为什么 swap1() 行为不同?

【问题讨论】:

【参考方案1】:

在同一个语句中有两个 array 的突变体。这通常是一个坏主意,并导致未定义的行为。这和你不想做的原因是一样的:

a = a++ + a++;

从人类的角度来看,很明显,您首先要删除一个项目,然后将其添加回已删除的数组中。

从编译器的角度来看,它必须做 3 件事来修改数组:1)读取原始数组的内容,2)修改内容,3)将它们写回。编译器必须在语句中这样做两次:

array.insert(array.removeAtIndex(0), atIndex:1)

您希望编译器这样做:

1) temp1 = array
2) temp1.removeItem
3) array = temp1
4) temp2 = array
5) temp2.addItem
6) array = temp2

但它确实做到了:

1) temp1 = array
2) temp2 = array
3) temp2.removeItem
4) array = temp2
5) temp1.addItem
6) array = temp1

编译器的操作顺序是允许的,这就是为什么你不应该在同一个语句中放置两个 mutator。

【讨论】:

我不确定这到底是怎么回事。由于其中一个 mutator 是另一个的参数,因此它们运行的​​顺序应该是明确定义的。 明确定义删除发生在插入之前,但没有明确定义插入是在删除之前还是之后存在的数组上操作。将数组视为要插入的第一个(隐藏)参数。在删除发生之前,可以读取该数组并将其放入堆栈(为插入调用做准备)。【参考方案2】:

这似乎是 Swift 处理数组的一个副作用。我相信 Apple 仍在尝试找出在 Swift 中处理可变/不可变数组的最佳方法,并且这种行为可能会改变(并且在 beta 版中已经改变。

这是一篇很好的文章(或详细介绍到目前为止的一些行为和变化的文章流):

http://blog.human-friendly.com/swift-arrays-the-bugs-the-bad-and-the-ugly-incomplete

【讨论】:

以上是关于为啥 Swift 数组插入和 removeAtIndex 操作行为不一致?的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 为啥 Process.arguments 返回空数组

为啥我不能在 Swift 中将对象数组返回给 UIStackView? [复制]

为啥我可以在 Swift 中更改 const 数组的成员? [复制]

为啥 SwiftyJSON 无法在 swift 3 中解析数组字符串

Swift(iOS)将UIImage数组插入子视图?

为啥 Swift4 会强制转换 UIButton 数组!到 [UIButton?] 类型?