Simple question, but I'm not sure how to test. Does a value type returned from a function always trigger a copy-on-write when it's modified, or are you modifying the exact same instance that was created inside?
For instance, I know this does not trigger a copy-on-write since you are modifying the object right when you create it.
struct Foo{
var value:String = "Initial"
}
var a = Foo()
a.value = "Changed" // <- This does *not* make a copy of Foo
However, here I'm not sure if the original value is thus orphaned
func fooFactory() -> Foo {
return Foo()
}
var b = fooFactory()
b.value = "Changed again" // <- Does this trigger a copy-on-write in the compiler?
The reason for this question is we have some very heavy structs that for reasons I can't get into here can't be classes, and our factory methods for those structs are doing a lot of the initial configuration, then the consumers are doing final configuration to them after they are returned from the factory.
I'm trying to see if we're actually creating tons of copies that we're essentially instantly discarding the moment they leave the factory.
Update:
Here's a test I made that appears to show that yes, a copy is made on the way out even before you've modified it (test 'c' below). Modifying that returned value (test 'd') however doesn't create an additional copy as the copy has already been made upon returning from the function.
That said, I'm not 100% sure this is a valid/accurate test so any feedback would be greatly appreciated.
struct Foo {
var value:Int = 0
}
var lastCheckedPointer:UnsafePointer<Foo>?
func printAddress(_ test: CustomStringConvertible, _ pointer:UnsafePointer<Foo>){
let observation = lastCheckedPointer.map{
$0 == pointer
? "Same instance"
: "Copy created"
} ?? "Initial instance"
print("Test \(test): \(pointer) - \(observation)")
lastCheckedPointer = pointer
}
func makeFoo() -> Foo {
var insideFoo = Foo()
printAddress("a", &insideFoo)
insideFoo.value = 1
printAddress("b", &insideFoo)
return insideFoo
}
var foo = makeFoo()
printAddress("c", &foo)
foo.value = 2
printAddress("d", &foo)
foo.value = 3
printAddress("e", &foo)
var foo2 = foo
foo2.value = 4
printAddress("f", &foo2)
Results:
Test a: 0x00007ffc735598d8 - Initial instance
Test b: 0x00007ffc735598d8 - Same instance
Test c: 0x000055cbfb6da1f0 - Copy created <-- This is the returned value
Test d: 0x000055cbfb6da1f0 - Same instance
Test e: 0x000055cbfb6da1f0 - Same instance
Test f: 0x000055cbfb6da1f8 - Copy created