When talking about object-oriented programming, most of us intuitively think about classes. In Swift, however, things are a bit different. While you can continue to use classes, Swift has a few other tricks up its sleeve that can change the way you think about software development. This is probably the most important shift in mindset when working with Swift, especially if you're coming from a more traditional object-oriented programming language like Ruby or Objective-C.
Value Types & Reference Types
The concept we tackle in this chapter is the differences between value types and reference types or, put differently, passing by value and passing by reference.
In Swift, instances of classes are passed by reference. This is similar to how classes are implemented in Ruby and Objective-C. It implies that an instance of a class can have several owners that share a copy.
Instances of structures and enumerations are passed by value. Every instance of a struct or enum has its own unique copy of data. And the same applies to tuples.
In the remainder of this chapter, I refer to instances of classes as objects and instances of structs and enums as values. This avoids unnecessary complexity.
An Example
It's important that you understand the above concept so let me explain this with an example.
We declare a class, Employee
, and create an instance, employee1
. We set the name
property of employee1
to Tom
. We then declare another variable, employee2
, and assign employee1
to it. We set the name
property of employee2
to Fred
and print the names of both Employee
instances.
Because instances of classes are passed by reference, the value of the name
property of both instances is equal to Fred
. Does that make sense? Or does this result surprise you?
In the second example, we declare a structure, Employee
, and repeat the steps of the first example. The results of the print statements is different, though.
By replacing class
with struct
, the outcome of the example changes in a significant way. An instance of a class, an object, can have multiple owners. An instance of a struct has one owner. When an instance of a struct is assigned or passed to a function, its value is copied. A unique instance of the struct is passed instead of a reference to the instance of the struct.
The moment employee1
is assigned to the employee2
variable, a copy of employee1
is made and assigned to employee2
. The values of employee1
and employee2
have no relation to one another apart from the fact that they are copies.
Benefits of Value Types
Swift uses value types extensively. Strings, arrays, dictionaries, and numbers are value types in Swift. That's not a coincidence. If you understand the benefits of value types, you automatically understand why these types are defined as value types in the Swift standard library. What are some of the benefits of value types?
Storing Immutable Data
Value types are great for storing data. And they're even better suited for storing immutable data. Assume you have a struct that stores the balance of the user's bank account. If you pass the balance to a function, that function doesn't expect the balance to change while it's using it. By passing the balance as a value type, it receives a unique copy of the balance, regardless of where it came from.
Knowing that a value won't change is a powerful concept in software development. It's a bold promise and, if used correctly, one that value types can keep.
Manipulating Data
Value types usually don't manipulate the data they store. While it's fine and useful to provide an interface for performing computations on the data, it's the owner of the value that manipulates the data stored by the value.
This results in a control flow that is transparent and predictable. Value types are easy to work with and, another great benefit, they behave great in multithreaded environments.
Why is that? If you fetch data from a remote API on a background thread and pass the data, as a value, from the background thread to the main thread, a copy is made and sent off to the main thread. Modifying the original value on the background thread won't affect the value that's being used on the main thread. Clean, simple, and transparent.
When to Use Value Types
While I hope I've convinced you to start using value types more often, they're not a good fit for every scenario. Value types are great for storing data. If you pass around the balance of someone's account in your application, you should not be using a class instance. What you're doing is passing around a copy of the current balance of the account.
But if you need to pass around the account itself, then you want to make sure the objects that have a reference to the account are working with the same account, the same instance. If the name of the account holder changes, you want the other objects that have a reference to the account to know about that change.
Value types and reference types each have their value and use. It would be unwise to choose one over the other. Andy Matuschak has a clear stance on the benefits of value types and reference types in software development and has given several presentations on the topic. Andy describes the objects of an application as a layer that operate on the value layer. That's a great way to put it.
Think of objects as a thin, imperative layer above the predictable, pure value layer. — Andy Matuschak