在 swift 中测试写时复制

Test Copy-on-write in swift

 import Foundation

 func address(o:UnsafeRawPointer) -> Int {
     return Int(bitPattern: o)
 }
 var originArray = [1,2,3]
 var firstArray = originArray


//q.append(4)
print(NSString.init(format: "originArray:%p", address(o: &originArray)))
print(NSString.init(format: "firstArray:%p", address(o: &firstArray)))

调试日志: originArray:0x100b087b0 firstArray:0x100b088c0

以上是我的测试 code.I 认为我不修改 originArray 喜欢追加或减少元素。他们应该指向相同的 address.but 为什么要尊重

您打印的是变量本身的地址,而不是它指向的数组缓冲区的地址。

您可以像这样获取数组缓冲区的地址:

var originArray = [1, 2, 3]
var firstArray = originArray

print("originArray: \(originArray.withUnsafeBytes { [=10=].baseAddress! })")
print("firstArray:  \(firstArray.withUnsafeBytes { [=10=].baseAddress! })")

现在打印相同的值,除非您修改其中一个数组。

您的代码正在打印数组缓冲区的地址(Array is a special case 将值传递给指针参数时)。但是,在 Swift 3 中,编译器假定 & 运算符的存在意味着缓冲区作为 mutable 内存传递,因此(不必要地)使它在传递其指针值之前是唯一的(通过复制),尽管该指针值作为 UnsafeRawPointer 传递。这就是您看到不同地址的原因。

如果删除 & 运算符并直接传递数组:

func address(_ p: UnsafeRawPointer) {
    print(p)
}

var originArray = [1, 2, 3]
var firstArray = originArray

address(originArray) // 0x00000000016e71c0
address(firstArray)  // 0x00000000016e71c0

您现在将获得相同的地址,因为编译器现在假定 address(_:) 不会修改传递的缓冲区的内存,因为它们被传递给 UnsafeRawPointer 参数。

在 Swift 4 中,此不一致已得到修复,编译器在将其指针值传递给 UnsafeRawPointer 参数之前不再使缓冲区唯一,即使在使用 & 时也是如此运算符,因此您的代码表现出预期的行为。

不过,值得注意的是,当将同一数组传递给多个指针参数时,上述方法不能保证产生稳定的指针值。

来自 Swift 博客 post “Interacting with C Pointers”:

Even if you pass the same variable, array, or string as multiple pointer arguments, you could receive a different pointer each time.

相信数组在两种情况下不能满足这个保证(可能还有更多):

  1. 如果数组正在查看非连续存储中的元素

    Swift 的 Array 可以查看非连续存储中的元素,例如当它包装 NSArray 时。在这种情况下,当将它传递给指针参数时,必须创建一个 new 连续缓冲区,因此会给你一个不同的指针值。

  2. 如果缓冲区在作为可变内存传递时被非唯一引用

    如前所述,当将数组传递给可变指针参数时,首先会使其缓冲区唯一以保留值语义,因为它假定函数将执行缓冲区的突变。

    因此,如果缓冲区被复制,您将获得一个不同的指针值,如果您将数组传递给一个不可变的指针参数。

虽然这两点在你给出的例子中都不适用,但值得记住的是编译器仍然保证你稳定的指向数组缓冲区的指针值传递给指针参数时。

对于保证可靠的结果,您应该使用 withUnsafeBytes(_:) method on a ContiguousArray:

var originArray: ContiguousArray = [1, 2, 3]
var firstArray = originArray

originArray.withUnsafeBytes { print([=11=].baseAddress!) } // 0x0000000102829550
firstArray.withUnsafeBytes { print([=11=].baseAddress!) }  // 0x0000000102829550

这是因为 withUnsafeBytes(_:) 被记录为接受:

A closure with an UnsafeRawBufferPointer parameter that points to the contiguous storage for the array. If no such storage exists, it is created.

并且ContiguousArray保证:

[it] always stores its elements in a contiguous region of memory

就像 Array 一样,ContiguousArray 使用写时复制以获得值语义,因此您仍然可以使用它来检查何时在进行突变时复制数组的缓冲区地点:

var originArray: ContiguousArray = [1, 2, 3]
var firstArray = originArray

originArray.withUnsafeBytes { print([=12=].baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print([=12=].baseAddress!) }  // 0x0000000103103eb0

firstArray[0] = 4

originArray.withUnsafeBytes { print([=12=].baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print([=12=].baseAddress!) }  // 0x0000000100e764d0