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 !!!

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?.fullNamehumanObj = nil

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

The fix

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] inguard 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] inreturn “\(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] inreturn “\(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.

capturing self along with closure parameters

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 for fresh articles. Connect with me on .

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

iOS and tvOS developer, dreamer, photographer 🤨