I was wondering if this behavior in the swift language is documented anywhere. I haven't been able to find it in the official documentation. It is best expressed in code:
var testBacking = CGPoint(x: 3, y: 5)
var testPoint:CGPoint {
get {
print("getter called")
return testBacking
} set {
print("setter called with newValue = \(newValue)")
testBacking = newValue
}
}
testPoint.x = 10 // getter called
// setter called with newValue = (10.0, 5.0)
As you can see, I am only setting the x
component of the computed structure testPoint
, and in doing so, swift automatically calls the getter first and pulls out the y
component and builds a complete structure that it then passes to the setter as newValue. This seems like appropriate behavior. My question is: Where is this behavior documented? Have I missed it, or is it simply not mentioned?
The way to understand this is to consider a similar but simpler case without your extra complications. Let's just talk about this:
struct S {
var name = "matt"
}
var s = S()
s.name = "mogelbuster"
How does that work? What happens when we set s.name
? First, pull out the current value of s
; then, we set its name
property; then, we set the value of s
to this new struct. (You can easily confirm that last part by putting a setter observer on s
.)
Setting a struct's property by way of a reference, then involves getting the reference's value (the struct), setting the property, and setting the reference's value with the new struct. That's because a struct is a value type. It cannot be mutated in place, so setting a property by way of a reference to a struct involves setting into the reference.
What you are doing with testPoint
is merely a computed-variable version of the same process. The mere act of speaking of testPoint.x
means that we must get testBacking
, to find out what it is. Thus, the getter is called. Then you set into testPoint.x
, thus calling the setter to write the new value back into testBacking
.
Note that the same thing would not be true if we were working with a class. Here's a variation on your original example:
class PointHolder {
var point = CGPoint(x:3, y:5)
var x : CGFloat {
get { return point.x }
set { point.x = newValue }
}
}
var testBacking = PointHolder()
var testPoint:PointHolder {
get {
print("getter called")
return testBacking
} set {
print("setter called with newValue = \(newValue)")
testBacking = newValue
}
}
testPoint.x = 10 // getter called, but _not_ setter
In the last line, the testPoint
setter is not called — because a class instance is a reference type and is mutable in place. testBacking
is changed without setting, because what we got from testBacking
when we said testPoint.x
, and the getter was called, is a reference; a change to this changes the thing testBacking
points to without setting into it.