Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use [weak self] in PromiseKit blocks?

PromiseKit states the following on their website:

Should I be concerned about retain cycles?

tl;dr: it’s safe to use self in promise handlers.

This is safe:

somePromise.then {
    self.doSomething()
}

Provided somePromise resolves, the function passed to then will be released, thus specifying [weak self] is not necessary.

Specifying [unowned self] is likely dangerous.

You’re telling me not to worry about retain cycles?!

No, it’s just that by default you are not going to cause retain cycles when using PromiseKit. But it is still possible...

Does this mean I should never use [weak self] in PromiseKit blocks? Are there ever situations where I would still need to use [weak self]? How exactly is it preventing a retain cycle?

like image 472
Senseful Avatar asked Sep 01 '16 21:09

Senseful


People also ask

Do you always need weak self?

Using [weak self] is only required within situations in which capturing self strongly would end up causing a retain cycle, for example when self is being captured within a closure that's also ultimately retained by that same object.

Where do we use weak self?

In Swift, [weak self] prevents closures from causing memory leaks in your application. This is because when you use [weak self], you tell the compiler to create a weak reference to self. In other words, the ARC can release self from memory when necessary.

Why do we use unowned and weak self?

Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization. In general, be very careful when using unowned.


2 Answers

TL;DR: Continue using [weak self] in PromiseKit blocks to prevent objects from living longer than necessary.


There are a few things to note. First, there are 2 primary reasons to use [weak self] in a block:

  1. Prevent retain cycles
  2. Prevent objects living longer than necessary

Second, PromiseKit is creating a retain cycle when you call that block of code. self is holding on to somePromise usually, and somePromise is holding on to self. The reason that they say you shouldn't be concerned with this retain cycle is because the retain cycle is going to get broken automatically by PromiseKit. When then is released, somePromise will no longer hold on to self, thus breaking the retain cycle.

So we know we don't need to worry about problem #1 with PromiseKit blocks, but what about problem #2?

Imagine a view controller fires off a network request promise and that it's going to take 30 seconds until this promise is resolved. Now before it's resolved, the user presses the back button. Normally UIKit is going to deallocate the view controller since it is no longer on screen and the system can save resources. However, since you referenced self in the promise, it can no longer be deallocated. This means the view controller is going to live 30 seconds longer in memory than is necessary.

The only way to resolve problem #2 is by using [weak self] inside the block.

Note: One could argue that when your view controller backs out, you should cancel the ongoing promise anyways so that it releases the hold on self. However, figuring out when a view controller should be deallocated is not a simple task. It's much easier to let UIKit handle the logic for you and if you do indeed need to do anything when a view controller is deallocated, implementing it in the view controller's dealloc method. This won't work if the block is holding on to the view controller strongly.


Update: Looks like one of the authors did talk about this and clarified the reasons for making those recommendations:

In fact, retaining self is probably what you want so to allow the promise to resolve before self is deallocated.

like image 190
Senseful Avatar answered Oct 10 '22 17:10

Senseful


That documentation is merely saying that you don't have to worry about PromiseKit introducing "strong reference cycles" (previously known as "retain cycles") because when the promise is fulfilled and the block finishes running, those strong references are automatically resolved for you. The choice of strong vs weak references is solely up to you.

For example, there's no need to keep a strong reference to a dismissed view controller if you are simply updating UI elements on scene that no longer exists. You'd use weak in that scenario. But sometimes you want strong reference (e.g. you may want to update the underlying model to reflect the success or failure of the promise).

Bottom line, all they're saying is that you shouldn't let PromiseKit dictate the strong vs weak references, but rather it should be driven by your app's broader design requirements. The only hard rule re PromiseKit is that you should avoid unowned.

like image 25
Rob Avatar answered Oct 10 '22 19:10

Rob