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
source to share
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 wrapsNSArray
. 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
source to share
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.
source to share