Functional programming: Closure reference cycle and fix

Here, I will try to explain an example of closure reference cycle. Follow the following article to understand more about closures in swift.

The reference cycle looks rusty !!!
Article_1 link → Functional swift : All about closures.
Article_2 link → Lazy var in iOS swift.

Please read the above two articles to continue.


Assuming you have a good knowledge in functional programming and closures, let’s begin.

Consider a class Human . It is having an init method which accepts a firstName and lastName as parameters. It also has a lazy var closure called fullName which accepts nothing and returns a String. It is of type ()->String .

closure reference cycle.

Here, when we call the init method , a new Human object is created . We assign this object to an optional type humanObj .

In the next line, when we set the value of humanObj to nil , the deinit{} method will get called. You can see the console printing the text given inside the deinit method.

Now, try to acces the fullName closure after creating the object. Then try to set the object to nil .

var humanObj:Human? = Human(firstName: “John”, lastName: “Doe”)
let fullName = humanObj?.fullName
humanObj = nil

The deinit{} method will not be called as the closure holds a strong reference to self.

deinit method will get called when an object is deallocated.

The fix

We can break this strong reference cycle using self in the capture list with either a weak or an unowned reference. If you don’t know what a capture list is, go through the article mentioned at the top.

weak

A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. This behaviour prevents the reference from becoming part of a strong reference cycle.

Since a weak reference may be nil, the variable captured becomes optional. Therefore, we should use a guard to safely unwrap it:

lazy var fullName:() -> String = { [weak self] in
guard let weakSelf = self else { return “”}
return “\(weakSelf.firstName) \(weakSelf.lastName)”
}

Unowned

Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime

lazy var fullName:() -> String = { [unowned self] in
return “\(self.firstName) \(self.lastName)”
}

This means that we should use unowned just when we are sure that the reference will never be nilinside the closure, otherwise the app would crash. So, guard check is not required here.

We can use weak and unowned with any variable in the capture list and we can also combine with the aliases:

lazy var fullName:() -> String = { [unowned unownedSelf = self] in
return “\(unownedSelf.firstName) \(unownedSelf.lastName)”
}

How to capture self with closures having arguments?

Well, you should capture the self before the internal closure parameter or before the parenthesis of internal parameters. Check the code below. It explains how to capture self in a single parameter case. If you have multiple parameters, you may also use parenthesis.

capturing self along with closure parameters

IMPORTANT: For non-escaping closures, we don’t have to worry about capturing the self using weak or unowned keywords. It matters only for escaping closures.

Exceptional cases:

Although it is good practice to capture self weakly in closures, it is not always necessary. Closures that are not retained by the self can capture it strongly without causing a retain cycle. A few common examples of this are:

Working with DispatchQueue, animation closures in GCD

DispatchQueue.main.async {
self.doSomething() // Not a retain cycle
}
UIView.animate(withDuration: 1) {
self.doSomething() // Not a retain cycle
}

Another interesting place where self would not need to be captured strongly is in lazy variables, that are not closures, since they will be run once (or never) and then released afterwards:

lazy var fullName = {
return self.firstName + " " + self.lastName
}() // not retained. no need to use self with weak or unowned

However, if a lazy variable is a closure, it would need to capture self weakly/unowned. The fullName:()->String closure in the Human class is a good example for this.

Enjoy!!

If you enjoyed reading this post, please share and recommend it so others can find it 💚💚💚💚💚💚 !!!!

You can follow me on Medium for fresh articles. Connect with me on LinkedIn.

If you have any comment, question, or recommendation, feel free to post them in the comment section below!