Value Types Are More Valuey Than You Think
Arrays are value types in Swift. This means, of course, they are copied on assignment instead passed by reference:
let foo = [1,2,3]
var bar = foo
bar = bar + [4,5]
println(foo) //=> [1,2,3]
println(bar) //=> [1,2,3,4,5]
But it turns out their valueness goes deeper than that. I’ve always been highly suspicious of mutating
methods, worried that I’d have to be extra vigilant not to have things modified “behind my back”:
class MyClass{
var elements:[String] = []{
didSet{ update() }
}
}
let foo = MyClass()
foo.elements = ["Hello"] //update() gets called
foo.elements.append("World") //what about now?
You can see my concern, I think. In the first example, I’m explicitly assigning to my property. Becasue it’s a value type, it gets copied. Becasue there’s assignment, the didSet
handler gets called. Beautiful!
In the second example, however, I’m calling append
on the array — a mutating
method. There’s no explicit assignment. If this were a reference type, I’d be modifing it “in place” behind MyClass
’s back. elements
would never get assigned to, and update()
would never get called.
But Swift is way cooler than that.
It turns out mutating methods don’t let you modify a value type “in place”. From The Swift Programming Language, we learn that when we mark a method as mutating
:
[T]he method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends.
Thus it would seem a mutating method doesn’t actually modify self
. It modifies a temporary proxy of self
. Then, when the method returns, implicitly assigns that back to self
. And so:
- Values stay values…
- Because even with
mutating
they can’t change without assignment… - And thus can’t be modified behind an observers back.
Nice!