Test Copy-on-write in fast

 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)))

      

Debug log: originArray: 0x100b087b0 firstArray: 0x100b088c0

Above, this is my test code. I think I am not modifying originArray, which kind of adds or decreases an element. they must indicate the same address. But why respect

+3


source to share


2 answers


Your code prints the addresses of the array buffers ( Array

is a special case when passing a value to a pointer parameter), However, in Swift 3, the compiler assumed that the presence of the operator &

meant the buffer was passed as volatile memory, so (unnecessarily) made it unique (by copying) before passing its pointer value, even though the pointer value is passed as UnsafeRawPointer

. This is why you see different addresses.

If you remove the operator &

and pass the arrays directly:

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

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

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

      

You will now get the same addresses as the compiler now assumes address(_:)

it will not modify the memory of the passed buffers as they are passed in the parameter UnsafeRawPointer

.

In Swift 4, this inconsistency is fixed and the compiler no longer makes the buffer unique before passing its pointer values ​​to a parameter UnsafeRawPointer

, even when using an operator &

, so your code demonstrates the expected behavior.

It is worth noting, though, that the above method does not guarantee stable pointer values ​​when passing the same array into multiple pointer parameters.

From Swift's blog post " Interacting with C Pointers :

Even if you pass the same variable, array, or string as multiple pointer pointers, you can get different pointers each time.

I believe this guarantee cannot be solved for arrays in two cases (there may be more):

  • If the array is looking at items in non-contiguous storage

    Swift Array

    can view items in non-contiguous storage, for example when it wraps NSArray

    . In this case, passing it to the pointer parameter must create a new contiguous buffer, so it will give you a different pointer value.

  • If the buffer is not uniquely referenced when passed as mutable memory

    As mentioned earlier, when passing an array into a mutable pointer parameter, its buffer will be unique at first to preserve the semantics of the value, since it was assumed that the function would mutate the buffer.

    Therefore, if the buffer was copied, you would get a different pointer value if you passed the array to the immutable pointer parameter.



While neither of these two points apply in the example you give, it is worth keeping in mind that the compiler still does not guarantee you stable pointer values ​​in the array buffer when passing to pointer parameters.

For guaranteed results, you must use the method withUnsafeBytes(_:)

on ContiguousArray

:

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

originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000102829550
firstArray.withUnsafeBytes { print($0.baseAddress!) }  // 0x0000000102829550

      

This is because of what is being withUnsafeBytes(_:)

documented as accepting:

A closure with a parameter UnsafeRawBufferPointer

indicating continuous storage for the array. If such a store does not exist, it is created.

And it ContiguousArray

ensures that:

[it] always stores its elements in contiguous memory

And just like Array

, it ContiguousArray

uses copy-on-write to have value semantics, so you can still use it to check when the array buffer is being copied when a mutation occurs:

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

originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print($0.baseAddress!) }  // 0x0000000103103eb0

firstArray[0] = 4

originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print($0.baseAddress!) }  // 0x0000000100e764d0

      

+3


source


You are printing the address of the variable itself, not the address of the array buffer it points to.

You can get the address of the array buffers:



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

print("originArray: \(originArray.withUnsafeBytes { $0.baseAddress! })")
print("firstArray:  \(firstArray.withUnsafeBytes { $0.baseAddress! })")

      

Now the same value is printed unless you modify one of the arrays.

+1


source







All Articles