在这种情况下,Swift是否会复制突变?

时间:2016-12-19 18:54:43

标签: swift memory

基本上我想要的是类属性的临时别名,以提高可读性。

我遇到以下代码描述的情况,我看不到一个简单的解决方案。我想避免的是y被复制到变异上然后被复制回来。 重命名y会大大降低实际算法的可读性。

Swift编译器是否足够智能,不能实际分配新内存,我怎么能知道呢?

如果没有,如何防止复制?

class myClass {
    var propertyWithLongDescriptiveName: [Float]

    func foo() {
        var y = propertyWithLongDescriptiveName
        // mutate y with formulas where y corresponds to a `y` from some paper
        // ...
        propertyWithLongDescriptiveName = y
    }
    // ...
}

2 个答案:

答案 0 :(得分:4)

struct Array是Swift中的值类型,这意味着它们始终是 分配给另一个变量时复制。但是,每个struct Array 包含指向实际的指针(在公共接口中不可见) 元素存储。因此

之后
var a = [1, 2, 3, 4]
var b = a

ab都是(形式上独立的)值,但指向同一元素存储的指针。 只有当其中一个发生变异时,才会生成元素存储的副本。 这被称为"复制写入"例如在

中解释

所以

之后
b[0] = 17

ab是带有指向不同(独立)元素存储的指针的值。 b的进一步变异不会再次复制元素存储 (除非将b复制到另一个变量)。最后,如果你指定 价值回归

a = b

释放a的旧元素存储,并且这两个值都是指向同一存储的指针。

因此在你的例子中:

    var y = propertyWithLongDescriptiveName
    // ... mutate y ...
    propertyWithLongDescriptiveName = y

元素存储的副本只做一次(假设 您不能将y复制到其他变量中。

如果数组大小没有改变,那么可能的方法可能是

var propertyWithLongDescriptiveName = [1.0, 2.0, 3.0, 4.0]

propertyWithLongDescriptiveName.withUnsafeMutableBufferPointer { y in
    // ... mutate y ...
    y[0] = 13
}

print(propertyWithLongDescriptiveName) // [13.0, 2.0, 3.0, 4.0]

withUnsafeMutableBufferPointer()用一个调用关闭 UnsafeMutableBufferPointer到元素存储。 UnsafeMutableBufferPointerRandomAccessCollection和 因此提供了类似阵列的接口。

答案 1 :(得分:2)

不,Swift编译器并不那么聪明。您只需要进行一项小测试即可了解它的作用:

class MyClass {
    var propertyWithLongDescriptiveName: [Float] = [1,2]

    func foo() {
        var y = propertyWithLongDescriptiveName
        y[0] = 3 // copied an mutated

        print(y)                               // [3,2]
        print(propertyWithLongDescriptiveName) // [1,2]
    }
}

let mc = MyClass()
mc.foo()

你有2个选择:

  • propertyWithLongDescriptiveName更改为NSMutableArray,这是一种参考类型
  • 接受copy-and-mutate的开销成本进行交易,以提高算法的可读性。在许多情况下,与算法相比,内存分配成本最低。