Hey #swiftlang peeps: How do you create an var that holds an immutable Array? As in a var that you can assign different immutable Arrays to?
— Mike R. Manzano (@bffmike) October 17, 2014
This is an interesting question. As a developer coming from Objective-C and the Cocoa frameworks, we're used to distinguishing between
NSMutableArray. It's generally bad practice to expose an
NSMutableArrayas a public property on an object.
The problem is that another part of the app could modify the contents of the
pizzaMenuarray without the knowledge of the
Pizzeriastill points to the same object it always has; from its perspective, nothing has changed. This is bad.
Instead, a better practice is to only expose an immutable
NSArray. This way, whenever any change happens, the setter method will be called. The
Pizzeriais always aware of what's happening.
In Swift, the
Arraytype is built into the language. Though you can still use
NSMutableArray, it's often not necessary; the native Array type is plenty capable.
There are two types of objects in Swift: value types and reference types. Reference types are like traditional Objective-C objects—multiple variables can reference the same object. This was the problem in the examples above above: both the
Pizzeriaobject and external code referenced the same object, so we had to expose an
NSArraythat does not have APIs for changing its contents.
Value types are identified by their value. They also exist in Objective-C, but only for C types (structs and primitives). If two
CGRectvariables have the same value, it simply means that the two rects contain the same data. A change to one rect will never change the value of another rect stored in a separate variable.
While Objective-C requires that all objects be reference types, Swift objects can be reference or value types. This means that Swift
structs can have methods just like
classes can. Swift's built-in collections (
Dictionary) are value types.
Because they are value types, two variables can never reference the "same" array. Every time an array is assigned to a variable, it gets a new, separate copy1 which cannot be affected by any other part of the code.
This means there is no need for a separate mutable collection type in Swift. Mutating a value type is always safe because changing a struct only affects the values of that specific variable. That what it means to be a value type. Modifying a Swift
Arraynever affects any other variable, anywhere.
Let's see how this affects our pizzeria example.
Note that even though
pizzeria.pizzaMenuis a mutable
varproperty, modifying the array we get back from the pizzeria has no effect on the pizzeria's copy of the data. They are separate arrays, separate values, and we can't change their copy unless we go through the setter.
In fact, this holds true even if we avoid assigning the array to an intermediate variable! In Objective-C, you simply cannot modify a value type held by another object. Many iOS developers have discovered this, to their frustration:
But in Swift, not only does this work, but it guarantees that the setter will be called! Whenever you call a mutating2 method on a value type, the end result is assigned back into the receiving variable (or expression). So calling
obj.someInts.append(1)causes a new value to be constructed, which is then assigned back into the
Because Swift arrays and dictionaries can never be shared, there is no distinction between mutating an existing collection and re-assigning a new collection. The behavior of the code is exactly the same. In either case, the owner's setter method is called whenever the array is modified.
So to answer the original question, there is no syntax to specify a variable that holds an immutable array because there is nothing that such syntax would add. Swift addresses the issues that made
NSMutableArraynecessary in the first place. If you need a shared array, you can still use the Cocoa types. In every other case, Swift's solution is safer, simpler, and more concise.
1: Note that the implementation is lazy, only performing an actual copy when really necessary. ↩
2: Methods on structs and enums in Swift must be labeled
mutatingif they're going to modify any data. Otherwise, they cannot be called on a value stored in a