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?
Weak References
To break strong reference cycles, you can specify the relationship between reference counted objects as weak.
Unless otherwise specified, all references are strong and impact reference counts. Weak references, however, don’t increase the reference count of an object.
In other words, weak references don’t participate in the lifecycle management of an object. Additionally, weak references are always declared as optional types. This means when the reference count goes to zero, the reference can automatically be set to nil.

In the image above, the dashed arrow represents a weak reference. Notice how the reference count of object1 is 1 because variable1 refers to it. The reference count of object2 is 2, because both variable2 and object1 refer to it.
While object2 references object1, it does so weakly, meaning it doesn’t affect the strong reference count of object1.
When both variable1 and variable2 go away, object1 will have a reference count of zero and deinit will run. This removes the strong reference to object2, which subsequently deinitializes.
Back in the Phone class, change the owner declaration to match the following:
weak var owner: User?
This breaks the User to Phone reference cycle by making the owner reference weak.

Build and run again. Now user and phone deallocate properly once the runScenario() method exits scope.
Unowned References
There is another reference modifier you can use that doesn’t increase the reference count: unowned.
What’s the difference between unowned and weak? A weak reference is always optional and automatically becomes nil when the referenced object goes away.
That’s why you must define weak properties as optional var types for your code to compile: The property needs to change.
Unowned references, by contrast, are never optional types. If you try to access an unowned property that refers to a deinitialized object, you’ll trigger a runtime error comparable to force unwrapping a nil optional type.

Time to get some practice using unowned.
Add a new class CarrierSubscription at the end of MainViewController.swift:
class CarrierSubscription {
let name: String
let countryCode: String
let number: String
let user: User
init(name: String, countryCode: String, number: String, user: User) {
self.name = name
self.countryCode = countryCode
self.number = number
self.user = user
print("CarrierSubscription \(name) is initialized")
}
deinit {
print("Deallocating CarrierSubscription named: \(name)")
}
}
CarrierSubscription has four properties:
- Name: Name of the subscription.
- CountryCode: Country of the subscription.
- Number: Phone number.
-
User: Reference to a
Userobject.
Who’s Your Carrier?
Next, add the following to User after the name property:
var subscriptions: [CarrierSubscription] = []
This adds a subscriptions property, which holds an array of CarrierSubscription objects.
Also, add the following to the top of the Phone class, after the owner property:
var carrierSubscription: CarrierSubscription?
func provision(carrierSubscription: CarrierSubscription) {
self.carrierSubscription = carrierSubscription
}
func decommission() {
carrierSubscription = nil
}
This adds an optional CarrierSubscription property and two new methods to provision and decommission a carrier subscription on the phone.
Next, add the following to init inside CarrierSubscription, just before the print statement:
user.subscriptions.append(self)
This adds CarrierSubscription to the user’s array of subscriptions.
Finally, add the following to the end of runScenario():
let subscription = CarrierSubscription(
name: "TelBel",
countryCode: "0032",
number: "31415926",
user: user)
iPhone.provision(carrierSubscription: subscription)
This creates a CarrierSubscription for user and provisions iPhone with it.
Build and run. Notice the console printout:
User John was initialized
Phone iPhone Xs was initialized
CarrierSubscription TelBel is initialized
Again, you see a reference cycle: Neither user, iPhone or subscription gets deallocated at the end.
Can you find where the issue is now?

Break the Chain
Either the reference from user to subscription or the reference from subscription to user should be unowned to break the cycle. The question is, which of the two to choose. This is where a little bit of knowledge of your domain helps.
A user owns a carrier subscription, but, contrary to what carriers may think, the carrier subscription does not own the user.
Moreover, it doesn’t make sense for a CarrierSubscription to exist without an owning User. This is why you declared it as an immutable let property in the first place.
Since a User with no CarrierSubscription can exist, but no CarrierSubscription can exist without a User, the user reference should be unowned.
Change the user declaration in CarrierSubscription to the following:
unowned let user: User
user is now unowned, breaking the reference cycle and allowing every object to deallocate. Build and run to confirm.

Reference Cycles With Closures
Reference cycles for objects occur when properties reference each other. Like objects, closures are also reference types and can cause cycles. Closures capture, or close over, the objects they operate on.
For example, if you assign a closure to a property of a class, and that closure uses instance properties of that same class, you have a reference cycle. In other words, the object holds a reference to the closure via a stored property. The closure holds a reference to the object via the captured value of self.
Add the following to CarrierSubscription, just after the user property:
lazy var completePhoneNumber: () -> String = {
self.countryCode + " " + self.number
}
This closure computes and returns a complete phone number. The property is lazy, meaning that you’ll delay its assignment until the first time you use the property.
This is necessary because it’s using self.countryCode and self.number, which aren’t available until after the initializer runs.
Add the following line at the end of runScenario():
print(subscription.completePhoneNumber())
Accessing completePhoneNumber() will force the closure to run and assign the property.
Build and run, and you’ll notice that user and iPhone deallocate, but CarrierSubscription does not, due to the strong reference cycle between the object and the closure.

Capture Lists
Swift has a simple, elegant way to break strong reference cycles in closures. You declare a capture list in which you define the relationships between the closure and the objects it captures.
To illustrate how the capture list works, consider the following code:
var x = 5
var y = 5
let someClosure = { [x] in
print("\(x), \(y)")
}
x = 6
y = 6
someClosure() // Prints 5, 6
print("\(x), \(y)") // Prints 6, 6
x is in the closure capture list, so you copy x at the definition point of the closure. It’s captured by value.
y is not in the capture list, and is instead captured by reference. This means that y will be whatever it is when the closure runs, rather than what it was at the point of capture.
Capture lists come in handy for defining a weak or unowned relationship between objects used in a closure. In this case, unowned is a good fit, since the closure cannot exist if the instance of CarrierSubscription has gone away.