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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
ARC and Memory Management in Swift
30 mins
- Getting Started
- Is That in Scope?
- An Object’s Lifetime
- Reference Cycles
- Checking Your References
- Hold the Phone(s)
- Weak References
- Unowned References
- Who’s Your Carrier?
- Break the Chain
- Reference Cycles With Closures
- Capture Lists
- Capture Your Self
- Using Unowned With Care
- Disarming the Trap
- Now for Something Different
- Finding Reference Cycles in Xcode 10
- Where Is That Leak?
- Bonus: Cycles With Value Types and Reference Types
- Reference and Value
- Where to Go From Here?
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!