Reference vs Value Types in Swift: Part 1/2

Eric Cerney

Value? Reference? Shared? Copy? Get your swift variables under control!

Value? Reference? Shared? Copy? Get your swift variables under control!

If you’ve been keeping up with the sessions from WWDC 2015, you might have noticed a real emphasis on re-thinking code architecture in Swift. One of the biggest differences developers note when coming to Swift from Objective-C is the heavy preference of value types over reference types.

This two-part series illustrates the differences between each type and shows you when each type is appropriate to use. You’ll learn about the main concepts of each type in Part 1, while in Part 2 you’ll work through a real-world problem as you learn more advanced concepts and discover some subtle but important points about both types.

Whether you’re coming from a traditional Objective-C background, or you’re more versed in Swift, you’re sure to learn something about the ins and outs of typing in Swift.

Update 10/15/16: This tutorial has been updated for Xcode 8 and Swift 3.

Getting Started

First, create a new playground: in Xcode, select File\New\Playground… and name the playground ValueSemanticsPart1. You can select either platform since this tutorial is platform-agnostic and only focuses on the Swift language aspects.

Click Next, choose a convenient location to save the playground and click Create to open it.

Reference Types vs. Value Types

So what’s the core difference between these two types? The quick-and-dirty explanation is that value types keep a unique copy of their data, while reference types share a single copy of their data.

Swift represents a reference type as a class. This is similar to Objective-C, where everything that inherits from NSObject is stored as a reference type.

There are many kinds of value types in Swift, such as struct, enum, and tuples. You might not realize that Objective-C also uses value types in number literals like NSInteger or even C structures like CGPoint.

To better understand the difference between the two, it’s best to start out with what you’ll recognize from Objective-C: reference types.

Reference Types

In Objective-C — and most other object-oriented languages — you hold references to objects. In Swift, however, you use class which is implemented using reference semantics.

Add the following to your playground:

// Reference Types:
 
class Dog {
  var wasFed = false
}

The above class represents a pet dog and whether or not the dog has been fed. Create a new instance of your Dog class by adding the following:

let dog = Dog()

This simply points to a location in memory that stores dog. To add another object to hold a reference to the same dog, add the following:

let puppy = dog

Because dog is a reference to a memory address, puppy points to the exact same address. Feed your pet by setting wasFed to true:

puppy.wasFed = true

puppy and dog both point to the exact same memory address.

value-reference

Therefore you’d expect any change in one to be reflected in the other. Check that this is true by viewing the property values in your playground:

dog.wasFed     // true
puppy.wasFed   // true

Changing one named instance affects the other since they both reference the same object. This is exactly what you’d expect in Objective-C.

Value Types

Value types are referenced completely differently than reference types are. You’ll explore this with some simple Swift primitives.

Add the following Int variable assignments and the corresponding operations to your playground:

// Value Types:
 
var a = 42
var b = a
b+=1
 
a    // 42
b    // 43

What would you expect a and b to equal? Clearly, a equals 42 and b equals 43. If you’d declared them as reference types instead, both a and b would equal 43 since both would point to the same memory address.

The same holds true for any other value type. In your playground, implement the following Cat struct:

struct Cat {
  var wasFed = false
}
 
var cat = Cat()
var kitty = cat
kitty.wasFed = true
 
cat.wasFed        // false
kitty.wasFed      // true

This shows a subtle, but important difference between reference and value types: setting kitty‘s wasFed property has no effect on cat. The kitty variable received a copy of the value of cat instead of a reference.

value-value

Looks like your cat‘s going hungry tonight! :]

Although it’s much faster to assign a reference to a variable, copies are almost as cheap. Copy operations run in constant O(n) time since they use a fixed number of reference-counting operations based on the size of the data.

This performance hit may seem like a reason to always use reference types, but Part 2 of this series shows you the clever methods in Swift that optimize these copy operations.

Mutability

var and let function differently for reference types and value types. Notice that you defined dog and puppy as constants with let, yet you were able to change the wasFed property. How’s that possible?

For reference types, let means the reference must remain constant. In other words, you can’t change the instance the constant references, but you can mutate the instance itself.

For value types, let means the instance must remain constant. No properties of the instance will ever change, regardless whether the property is declared with let or var.

It’s much easier to control mutability with value types. To achive the same immutability/mutability behavior with reference types, you’d need to implement immutable and mutable class variants such as NSString and NSMutableString.

What Type Does Swift Favor?

It may surprise you that the Swift standard library uses value types almost exclusively. The results of a quick search through the stdlib for public instances of enum, struct, and class in Swift 1.2, 2.0, and 3.0 show a bias in the direction of value types:

Swift 1.2:

  • struct: 81
  • enum: 8
  • class: 3

Swift 2.0:

  • struct: 87
  • enum: 8
  • class: 4

Swift 3.0:

  • struct: 124
  • enum: 19
  • class: 3

This includes types like String, Array, and Dictionary which are all implemented as structs.

Which to Use and When

Now that you know the difference between the two types, when should you choose one over the other?

There’s one case where the choice is forced upon you: many Cocoa APIs require NSObject subclasses, which forces you into using class. But other than that, you can use the following cases from Apple’s Swift blog to decide whether to use a struct / enum value type or a class reference type.

When to Use a Value Type

Generally speaking, use value types in the following instances:

Comparing instance data with == makes sense

“But of course,” you say. “I want every object to be comparable!”. But you need to consider whether the data should be comparable. Consider the following implementation of a point:

struct Point: CustomStringConvertible {
  var x: Float
  var y: Float
 
  var description: String {
    return "{x: \(x), y: \(y)}"
  }
}

Does that mean two variables with the exact same x and y members should be considered equal?

let point1 = Point(x: 2, y: 3)
let point2 = Point(x: 2, y: 3)

It’s clear that two Points with the same internal values should be considered equal. The memory location of those values doesn’t matter; you’re concerned about the values themselves.

Therefore, you’d need to conform to the Equatable protocol, which is good practice for all value types. This protocol defines only one function which you must implement globally in order to compare two instances of the object. This means that the == operator must have the following characteristics:

  • Reflexive: x == x is true
  • Symmetric: if x == y then y == x
  • Transitive: if x == y and y == z then x == z

Here’s a sample implementation of == for your Point:

extension Point: Equatable { }
func ==(lhs: Point, rhs: Point) -> Bool {
  return lhs.x == rhs.x && lhs.y == rhs.y
}

Copies should have independent state

Taking your Point example a little further, consider the following two Shape instances with their centers as two initially equivalent Points:

struct Shape {
  var center: Point
}
 
let initialPoint = Point(x: 0, y: 0)
let circle = Shape(center: initialPoint)
var square = Shape(center: initialPoint)

What would happen if you altered the centerpoint of one of the shapes?

square.center.x = 5   // {x: 5.0, y: 0.0}
circle.center         // {x: 0.0, y: 0.0}

Each Shape needs its own copy of a Point so you can maintain their state independent of each other. Could you imagine the chaos of all shapes sharing the same copy of a center Point? :]

The data will be used in code across multiple threads

This one’s a little more complex. Will multiple threads access this data? If so, will it really matter if the data isn’t equal across all threads at all times?

To make your data accessible from multiple threads and equal across threads, you’ll need to use a reference type and implement locking as well — no easy task!

If threads can uniquely own the data, using value types makes the whole point moot since each owner of the data holds a unique copy rather than a shared reference.

When to Use a Reference Type

Although value types are useful in many cases, reference types are still useful in the following situations:

Comparing instance identity with === makes sense

=== checks if two objects are exactly identical, right down to the memory address that stores the data.

To put this in real-world terms, consider the following: if your cubicle-mate swaps one of your $20 bills with another legitimate $20 bill, you don’t really care, as you’re only concerned about the value of the object.

However, if someone stole the Magna Carta and created an identical parchment copy of the document in its place, that would matter greatly because the inherent identity of the document is not the same at all.

You can use the same thought process when deciding whether to use reference types; usually there are very few times when you really care about the inherent identity — that is, the memory location — of the data. You usually just care about comparing the data values.

You want to create a shared, mutable state

Sometimes you want a piece of data to be stored as a single instance and accessed and mutated by multiple consumers.

A common object with a shared, mutable state is a shared bank account. You might implement a basic representation of an account and person as follows:

class Account {
  var balance = 0.0
}
 
class Person {
  let account: Account
  init(_ account: Account) {
    self.account = account
  }
}

If any joint account holders add money to the account, then the new balance should be reflected on all debit cards linked to the account:

let account = Account()
 
let person1 = Person(account)
let person2 = Person(account)
 
person2.account.balance += 100.0
 
person1.account.balance    // 100
person2.account.balance    // 100

Since Account is a class, each Person holds a reference to the account, and everything stays in sync.

Still Undecided?

If you’re not quite sure which mechanism applies to your situation, default to value types. You can always move to a class later with little effort.

value-struct

Consider, though, that Swift uses value types almost exclusively, which is mind-boggling when you consider that the situation in Objective-C is completely the reverse.

As a code architect under the new Swift paradigm, you need to do a bit of up-front planning as to how your data will be used. You can solve almost any situation with either value types or reference types — but using them incorrectly could result in tons of bugs and confusing code.

In all cases, common sense and a willingness to change your architecture when new requirements come up is the best approach. Challenge yourself to follow the Swift model; you just might turn out some nicer code than you originally thought!

Where to Go From Here?

You can download the complete playground with all the code in this article here.

By this point you’ve covered the differences between value types and reference types and when to use one over the other. In Part 2 of this tutorial series, you’ll work through a real-world problem and learn some advanced mechanics of value types.

If you have any comments or questions, feel free to join in the forum discussion below!

Eric Cerney

Eric is an iOS Software Engineer in San Francisco. After being acquired by Capital One, he likes to spend his days at work hanging out with Samuel L. Jackson and asking everyone "What's in your wallet?". Lately his main focus has been with Swift and gaining a deeper knowledge of programming languages at the core.

Outside iOS, his interests are tinkering with hardware (Raspberry Pi and Arduino), gaming, exploring San Francisco, and regretting endless Netflix marathons. You can find Eric on Twitter or his personal site.

Other Items of Interest

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 19 total!

Swift Team

... 16 total!

iOS Team

... 30 total!

Android Team

... 15 total!

macOS Team

... 11 total!

Apple Game Frameworks Team

... 10 total!

Unity Team

... 11 total!

Articles Team

... 11 total!

Resident Authors Team

... 11 total!