The Weak, the Strong, and the Unowned — Memory Management in Swift

Memory management scares most developers when it doesn’t work as expected. Apple’s Automatic Reference Counting (ARC) magically handles most memory management we used to do manually, but still often needs guidance to work well. Swift provides us with cool tools like capture lists, unowned, and weak references to do so. In this talk, Hector Matos discusses memory leaks and retain cycles — how these problem arise and how to fix them. We can then leverage Swift’s compiler magic to write clearer, more expressive, non-leaky code. Memory management issues, be gone!


Introduction (0:00)

Hi! I’m Hector Matos, a senior software engineer at Capital One, and I’ve been doing iOS for close to four years. I started back in manual memory management days, for all of you who fondly remember that time. I worked at a startup for about a year, then Apple for a year, followed by another startup, Monsoon, that was recently acquired by Capital One, where I work now. I worked with Swift since day one, and had the privilege of learning a lot about it when creating an entire app from the ground up with other Apple developers. You can find me on Twitter here, and I blog on KrakenDev, where I teach as much as I know about Swift.

Automatic Reference Counting (ARC) (2:02)

Strong, weak, and unowned all relate to ARC - teaching it how to handle all of our memory usage cases. It’s not garbage collection, which releases objects at runtime. Instead, it generates lines of code at compile time to release points or objects.

If you were around a few years ago, this should look familiar to you:

- (void)releaseTheKraken {
    Kraken *kraken = [[Kraken alloc] init]; //+1 retain
    [kraken release]; //now generated by ARC
}

It was terrible, and I’m glad it’s over. The last line is helpfully generated by ARC at compile time, but it gets more complex when using circular hierarchies. ARC is magical, but even magic has its limitations. When we start using fancy things like closures and blocks, ARC just doesn’t know where to stick those release messages at compile time. We have to let it know with weak and unowned, and that is why I move over to Swift and stay away from Objective-C (long live the old man).

Leaked Memory and Retain Cycles (3:58)

Weak and unowned are used to solve leaked memory and retain cycles so I’ll discuss them. Here’s what a retain cycle looks like:

Human-Heart Retain Cycle

You have two objects: a human and a heart. A human can’t live without a heart and a heart can’t live without a human. In code, for the human to speak to the heart and vice versa, we need each of them to reference each other (to keep it simple without delegation).

In this case, Human has a strong reference to Heart and Heart has a strong reference back up to Human. This is the definition of a retain cycle: when two objects have strong references to each other. Therefore, we can’t deallocate either one because the other is strongly referring to it.

To break the retain cycle, we use this:

Human-Heart Retain Cycle Broken!

Human can refer to Heart as its child, and Heart can have a weak reference back up to Human. As a result, deallocating one takes the retain count down to zero. Since Heart never actually retains Human, the retain cycle is broken.

Strong References (5:36)

In Swift, strong reference is the default when defining properties. A regular strong property in a regular class looks like so, where a Kraken keeps track of a yummy human.

//Kraken holds a strong reference to the yummy human.
class Kraken {
    var yummyHuman: Human
}

A distinction also exists between reference types and value types. Reference types are classes, whereas value types are enums or structs, which are copied on assignment. Value types don’t worry about deallocation, but classes do.

Closures also act as first-class citizens in Swift, and are reference types, acting like classes:

// The animation closure holds a strong reference to self
// self.retainCount is 1 here
UIView.animateWithDuration(0.3) {
    // self.retainCount is 2 here
    self.view.alpha = 0.0
}

Think of closures as classes — they capture values inside of them. In the case of something as simple as an animation, your parent (the closure) is capturing self in order for it not to get deallocated at some point in the future. The closure is stored to be called in the future. self cannot deallocate until the closure is released because the animation has a strong reference to self. As we know, you can’t deallocate an object unless the retain count gets down to zero.

Q: If the retained self in the closure is released outside, would self deallocate after the animation closure completes?

Hector: Yes, self cannot be deallocated in this case until the animation completes. Once the animation is done, it releases its hold of self, the reference count goes down to zero, self is deallocated, the pointer to it sets to nil, and the memory is freed. However, issues arise when the animation captures self and self gets released outside, but the animation is never called, leading to self never being able to deallocate — a memory leak.

Weak References (9:30)

Unlike strong references, weak references do not increase the retain count by one. Passing the object around doesn’t change the reference count. As a result, by nature, when used in closures, weak references are optional. Similarly to strong references, once the object is deallocated, the pointer to it is nullified. This maps directly to weak in Objective-C, which you might be familiar with.

When you drag an IBOutlet to your view controller, it is automatically weak. These examples are here to show you how to create a weak reference in Swift: you use the weak specifier.

class KrakensFace: UIView {
  @IBOutlet weak var razorSharpTeeth: UIView!
}

KrakenAPI.eat(yummyHuman, whenFinished: { [weak self] in
  self?.waitForNextMealTime()
})

In this example, your Kraken’s face has razor sharp teeth. Say you have a Kraken API that decides to eat the yummy human. If you want to create a weak reference to self, you have it here. Since it’s weak, it’s an optional, so in order for us to make any calls on it, we can optional chain into self:

self?.waitForNextMealTime()

Capture Lists - [weak self] (12:17)

[weak self] in scared me initially, but it’s actually simple. Well, the square brackets indicate of a capture list, which is just an array, with weak self being the only object inside. Being an array, a capture list can do more than just capture weak self. You can capture as many things as you’d like:

KrakenAPI.eat(yummyHuman, finished: { [weak self, unowned lovedOne = yummyHuman.lovedOne] in
    self?.waitForNextMealTime()
})

The capture list simply tells the compiler how to capture these values. In this example, self is captured weakly, and lovedOne is unowned.

Q: It might be irrelevant, but why do you have to use self in closures?

Hector: When accessing self’s properties, capturing self is a way of Apple to force you to think about the memory management when capturing self - since it increases its retain count by one. Same goes for calling a function of or on self. The compiler forces the developer to capture self to help write better code, keeping memory management in mind.

Q: Since weak references do not increment the reference count, what prevents those weakly referred to objects from disappearing completely right away? It seems like it would start by allocating the object and immediately throwing it away.

Hector: Weak is actually very dependent on strong. The minute you try to create a weak reference to anything in your code without anything having a strong reference, it will allocate and immediately deallocate, as you said. To prevent immediate deallocation, another object needs to strongly refer to the weak object; this is the premise of weak reference, and also why it’s optional.

To prevent weakly referred objects from deallocating while being used in a closure, we use a strong reference to the weak pointer, as we did in Objective-C. The weak reference is outside the block, but inside, to make certain it doesn’t deallocated, we hold a strong reference to the weak reference. In Swift this is done by using if-let unwrapping: if let reference = weakReference. This takes time to get the hang of.

Unowned References (17:17)

Unowned references are not the same as weak. Both do not increase the retain count, but unowned requires mutual dependence. weak will nullify the pointer whenever the reference is deallocated. Unowned’s Objective-C counterpart is unsafe_unretained. It will not nullify the pointer, and it does not increase the reference count. Unowned is very similar to unsafe_unretained, except it’s just a little bit safer. Behind the scenes, it has some extra checks that unsafe_unretained doesn’t. Nevertheless, since unowned references aren’t nullified by themselves, dangling pointers may result.

Unowned is the ability to have a pointer to a completely illegitimate object. When used in closures, unowned references act like they are implicitly unwrapped optionals. We know the difference between optional and implicitly unwrapped optional. Weak references are optionals, and unowned references behave like implicitly unwrapped optionals, so when you use them in closures you don’t have to optional chain them and you don’t have to unwrap them.

There are a couple of places where you would use them. Take the example of the human and the heart:

class Human {
    var heart: 💖
    func seeKrakenComing() {
        heart.haveHeartAttack()
    }
}
class 💖: Organ {
    unowned let human: Human
    init(human: Human) {
        self.human = human
    }
    func haveHeartAttack() {
        human.die()
    }
}
let human = Human()
human.heart = 💖(human: human)

A human cannot exist without a heart, and a heart cannot exist without a human. So when I’m creating my Human here, I want to give life to him and give him a Heart. When I initialize this Heart, I have to initialize it with a Human instance. To prevent the retain cycle here that we went over earlier (i.e. so they don’t have strong references to each other), your Human has a strong reference to the Heart and we initialize it with an unowned reference back to Human. This means Heart does not have a strong reference to Human, but Human does have a strong reference to Heart. This will break any future retain cycles that may happen.

This is actually a really good place to use unowned for the sole purpose that I can absolutely guarantee that this human cannot exist without the heart and that heart cannot exist without the human. I know that. I’m coding for that. That’s what I’m developing for. I can guarantee it.

Unowned and Core Foundation

Another place you can use unowned references is with Core Foundation objects. Core Foundation objects aren’t like Cocoa objects. They kind of manage their own lifetimes. When we use them in Swift, you may need to use unowned when you’re trying to take an unretained value or something like that. I’m fairly sure that would be a good place to use it.

Q: Is there any time where the compiler will complain if you make a weak or unowned variable?

Hector: No, not that I can think of. Any reference to a reference type can be weak, strong, or unowned. These keywords do only apply to references, however, not value types since those are copied on assignment.

Delegation and Weak References (Live Code) (24:43)

[Hector demoes how a retain cycle is created, what it would look like in Instruments, and how to solve it using a weak reference to the delegate in a sample app. He also discusses the idea of class-only protocols that allow for weak references to delegates.]

Q&A (35:09)

Q: As an old Objective-C developer, in my world, delegates should always, always, always be weak.

Hector: I completely agree with you. It’s always better to code safe. This also helps avoid retain cycles when your teammate, for instance, changes the code.

Q: Do you know if Android also has potential memory issues that require the developers to be actively involved in memory management?

Hector: Yes, it does indeed have its own memory issues, and has solutions similar to weak references to address them.

Q: A class protocol limits protocols to only be adopted by reference types. Why do we need to add that restriction if the protocol is only used by reference types?

Hector: Regular, non-class protocols can be used with classes, structs, and enums in Swift — reference types and value types. By limiting the protocol to only reference types, we can make references to objects that conform to it weak or strong. We couldn’t do this if the protocol was not class only because weak, strong, and unowned do not apply to value types


Hector Matos

Hector Matos

Raised by llamas in the great state of Texas, Hector grew to be an avid couch potato who likes spending his precious couch time playing The Legend of Zelda or yelling at the TV whilst watching Game of Thrones, and his other time with his lovely daughter and wife. When not vegging at home or blogging about Swift, you can find him sitting at the office writing mobile apps for iOS & Android for Capital One. With a particular penchant for great mobile UI/UX, Hector writes the code that makes the world go round.