ARC and Memory Management in Swift

In this tutorial, you’ll learn how ARC works and how to code in Swift for optimal memory management. You’ll learn what reference cycles are, how to use the Xcode 10 visual debugger to discover them when they happen and how to break them using an example of a reference cycle in practice. By Mark Struzinski.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Bonus: Cycles With Value Types and Reference Types

Swift types are reference types, like classes, or value types, like structures or enumerations. You copy a value type when you pass it, whereas reference types share a single copy of the information they reference.

This means that you can’t have cycles with value types. Everything with value types is a copy, not a reference, meaning that they can’t create cyclical relationships. You need at least two references to make a cycle.

Back in the Cycles project, add the following at the end of MainViewController.swift:

struct Node { // Error
  var payload = 0
  var next: Node?
}

Hmm, the compiler’s not happy. A struct value type cannot be recursive or use an instance of itself. Otherwise, a struct of this type would have an infinite size.

Change struct to a class:

class Node {
  var payload = 0
  var next: Node?
}

Self reference is not an issue for classes (i.e. reference types), so the compiler error disappears.

Now, add this to the end of MainViewController.swift:

class Person {
  var name: String
  var friends: [Person] = []
  init(name: String) {
    self.name = name
    print("New person instance: \(name)")
  }

  deinit {
    print("Person instance \(name) is being deallocated")
  }
}

And add this to the end of runScenario()

do {
  let ernie = Person(name: "Ernie")
  let bert = Person(name: "Bert")
  
  ernie.friends.append(bert) // Not deallocated
  bert.friends.append(ernie) // Not deallocated
}

Build and run. Notice that neither Bert nor Ernie is deallocated.

Reference and Value

This is an example of a mixture of value types and reference types that form a reference cycle.

ernie and bert stay alive by keeping a reference to each other in their friends array, although the array itself is a value type.

Make the friends array unowned and Xcode will show an error: unowned only applies to class types.

To break the cycle here, you’ll have to create a generic wrapper object and use it to add instances to the array. If you don’t know what generics are or how to use them, check out the Introduction to Generics tutorial on this site.

Add the following above the definition of the Person class:

class Unowned<T: AnyObject> {
  unowned var value: T
  init (_ value: T) {
    self.value = value
  }
}

Then, change the definition of friends in Person like so:

var friends: [Unowned<Person>] = []

And finally, replace the do block in runScenario() with the following:

do {
  let ernie = Person(name: "Ernie")
  let bert = Person(name: "Bert")
  
  ernie.friends.append(Unowned(bert))
  bert.friends.append(Unowned(ernie))
}

Build and run. ernie and bert now deallocate happily!

The friends array isn’t a collection of Person objects anymore, but instead a collection of Unowned objects that serve as wrappers for the Person instances.

To access the Person object within Unowned, use the value property, like so:

let firstFriend = bert.friends.first?.value // get ernie 

Where to Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.

You now have a good understanding of memory management in Swift and you know how ARC works. If you want to learn more about the debug tools in Xcode 10, watch this WWDC session or check out chapter 2 of iOS 10 by Tutorials.

If you want an even more in-depth look at weak reference implementation in Swift, check out Mike Ash’s blog post Swift Weak References. It covers how weak references in Swift differ from the Objective-C implementation, and how Swift actually keeps two counts under the hood: One for strong references and one for weak references.

Finally, if you are a raywenderlich.com subscriber, check out the iOS 10 Screencast: Memory Graph Debugger. These tutorial gives some great tips for getting the most out of the memory visualizer.

What do you think about the ARC approach? Share your thoughts in the comments!