CloseHomeAboutBlogCourse
Bob I’m an iOS instructor/blogger from S.Korea.July 14 • 5 min read • Edit

Swift Retention Cycle in Closures and Delegate

Let’s understand [weak self], [unowned self] , and weak var

Problem

When I encountered closures and delegate for the first time, I’ve noticed people put [weak self] in closures and weak var in front a delegate property. I’ve wondered why.

Prerequisite

This isn’t a tutorial for beginners. The following list is what I expect from my readers to know.

1.How to pass data between view controllers with Delegate

  1. Memory management in Swift with ARC
  2. Closures capture list
  3. Protocols as Type

Don’t worry if you aren’t confident with the materials above. I’ve covered all of them in one single Swift course called, Learn Swift with Bob.

Objectives

First, you will learn why we use weak var in delegate. Second, you will learn when to use [weak self]and [unowned self] in closures.

I like that the content is getting more advanced. We are growing together.

Retention Cycle in Delegate

First, let’s create a protocol called, SendDataDelegate.

protocol SendDataDelegate: class {}

Second, let’s create a class called SendingVC with a property whose type is SendDataDelegate?.

class SendingVC {
 var delegate: SendDataDelegate?
}

Last, lets assign the delegate to another class.

class ReceivingVC: SendDataDelegate {
 lazy var sendingVC: SendingVC = {
  let vc = SendingVC()
  vc.delegate = self // self refers to ReceivingVC object
  return vc
 }()

deinit {
 print("I'm well gone, bruh")
 }
}

You might be baffled by the lazy init method. Well, you could do your own research or you can wait for my next article. (Available now)

Now, let’s create an instance.

var receivingVC: ReceivingVC? = ReceivingVC()

Recap

First, receivingVC is an instance of ReceivingVC(). ReceivingVC() has a property, sendingVC.

Second, sendingVC is an instance of SendingVC(). SendingVC() has a property, delegate.

I’ve made a simple graph for you to visualize.

Strong reference cycle and memory leak

Make sure you are comfortable with the meaning of strong vs weak reference. If not, you may start with Make Memory Management Great Again.

There is a strong reference between ReceivingVC and SendingVC.

If you attempt the following code below, nothing happens.

var receivingVC = nil // not deallocated

Introducing weak var

The only thing that we need to do is, put weak in front of var delegate.

class SendingVC {
 weak var delegate: SendDataDelegate?
}

There is no such thing as weak let. When you use weak, just like the delegate property above, the property should be optional and mutable in order to set it as nil or assign value to the delegate property. Consequently, let is not allowed.

Let’s see how it looks now.

var delegate has a weak reference to ReceivingVC

Let’s attempt to get rid of it.

receivingVC = nil
// "I'm well gone, bruh"

You only need to use weak if the delegate is a class. Swift structs and enums are value types, not reference types, so they don't make strong reference cycles. If you are confused by the previous sentence, you may read Intro to Protocol Oriented Programming

Congratulations! You’ve completed the first objective. Let’s move on.

Retention Cycle in Closures

Now, let’s look at the second objective. Our goal is to find out why we use [weak self] within a closure block. First, let’s create a class called, BobClass. It contains two properties whose types are String and (() -> ())?

class BobClass {
 var bobClosure: (() -> ())?
 var name = “Bob”
 init() {
  self.bobClosure = { print(“Bob the Developer”) }
 }
 deinit {
  print(“I’m gone... ☠️”)
 }
}

Let us create an instance.

var bobClass: BobClass? = BobClass()

Let us visualize.

No reference cycle. Unidirectional

As you’ve noticed, the closure block is a separate entity from the entire class

Let us destroy.

bobClass = nil // I'm gone... ☠️

Everything works great. However, we don’t live in an ideal world. What if the closure block has a reference to the property?

init() {
 self.bobClosure = { print("\(self.name) the Developer") }
}

Let us visualize.

Strong reference cycle between the closure block and BobClass

Let us destroy

bobClass = nil // Not deallocated 😱

It’s urgent. We need do something about it

Capture List

There is one way for us to set the relationship “weak” from the closure block to the object (self). Let us introduce capture lists.

self.bobClosure = { [weak self] in
 print("\(self?.name) the Developer")
}

The closure block stole and copied the object (self). However, the closure had it weak.

Let us visualize.

Closure block has a weak reference to object, thus the property as well

If you don’t understand what [] does within the closure above, you may read Swift capture list and come back after.

There is something weird.

All of a sudden, the self (object) became an optional type based on self?.name. Here is why. The closure should be able to break the reference (green arrow) by setting self as nil within the closure block because the relationship is weak. Therefore, Swift automatically converts the type of self as optional.

Let’s attempt to deallocate

bobClass = nil // I'm gone...☠️

Closure deallocated

Congratulations, you’ve completed the second objective. But, there is just one more: Unowned.

Unowned

Some of you guys be like, “There is one more? Come on, Bob”. Yeah, there is one more. You’ve come a long way. Let us finish strong.

weak and unowned are the same. Except one thing. Unlike weak we’ve seen, unowned does not automatically convert self as optional within the closure block.

For example, if I create a normal instance instead of an optional type,

var neverNilClass: BobClass = BobClass()

There is no reason to use weak because if you do, the closure captures self as an optional type and you have to unwrap which is unnecessary like below.

self.bobClosure = { [weak self] in
  guard let object = self else {
    return
  }
  print("\(object.name) the Developer")
}

Instead, if you are 100% sure that self will never be nil, then just use

self.bobClosure = { [unowned self] in
  print("\(self.name) the Developer")
}

Source Code

About Me

iOS Developer from South Korea. Feel free to follow my story on Instagram or get serious on LinkedIn