Swift Closures Causing Strong Retain Cycle with Self

Swift closures causing strong retain cycle with self

Yes, that can still cause a retain cycle.

The simplest retain cycle is 2 objects that each have strong references to each other, but 3-way and larger retain cycles are also possible.

In your case, you have view controller who's view contains a button (a strong reference.) The button has a strong reference to a closure. The closure strongly references the view controller using self. So the view owns the button. The button owns the closure. The closure owns the view controller. If you dismiss the view controller (say it was a modal) then it SHOULD be deallocated. However, since you have this 3-way retain cycle, it won't be deallocated. You should add a deinit method to your view controller with a print statement and try it.

The solution is to add a capture list (The [weak self] bit) just like you did in your first example.

Note that a common pattern is to add a capture list, and then map the weak variable to a strong variable inside the closure:

let myClosure = { [weak self] in 
guard let strongSelf = self else { return }
//...
strongSelf.doSomething()
}

That way, if the closure is still active but the object that owns it was released, the guard statement at the beginning detects that self is nil and exits at the beginning of the closure. Otherwise you have to unwrap the optional every time you refer to it.

In certain cases it's also possible that the object in the capture list (self in these examples) could be deallocated in the middle of the closure being executed, which can cause unpredictable behavior. (Gory details: This is only when the closure is run on a different thread than the owner of the object in the capture list, but completion handlers are pretty commonly run on a background thread, so it does happen)

Imagine this:

let myClosure = { [weak self] in 

self?.step1() //1

//time-consuming code

self?.property = newValue //2

//more time-consuming code

self?.doSomething() //3

//even more time-consuming code

self?.doSomethingElse() //4
}

With the code above, if the closure is run on a background thread, its possible that self would still be valid at step 1, but by the time you execute step 2, self has been deallocated. The same goes for steps 3 and 4. By adding the guard strongSelf = self else { return } at the beginning of the closure you test at the entry of the closure to make sure self is still valid, and if it is, you make the closure create a strong reference that only lives as long as the closure takes to run, and that prevents self from being deallocated while the closure code is executing.)

Weird weak self and retain cycle behaviour

First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.

Let's try to walk through them one by one.

Printer1

As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action instance method as the closure to the Delayer, and since all closures are reference types, passing self.action will retain its surrounding scope (which is Printer1).

Printer2

Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.

Printer3

Here, a retain cycle is not created, because the self.weakAction property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2.

Printer4

First, you're capturing a weak reference to self, and then fetching welf?.action and passing the result into Delayer. Again, welf?.action is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action instance method will retain self. This is identical to Printer1.

Printer5

Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self is never directly referenced in the passed closure, it will not capture self in that scope, only the welf weak reference. This is pretty much identical to Printer2, but with a slightly different syntax.

Personally, I would opt for the Printer2 way (creating a new closure, retaining a weak reference to self and using that to call self?.action). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.

Why am I getting a retain cycle from passing weak self in to a static function's closure?

try comment this line

self?.configureCollectionView() 

mb it's the problem, because [weak self] is enough for fix retain in this closure

closure and retain cycle

You're correct. B is retaining A, but A is retaining as its b_code property a function that refers to (captures) B. That's a retain cycle.

I am trying to pass a closure from one class to another

No you're not. In this line

 a.b_code = someCode

the term someCode is not a closure. It is a method reference. That's the trouble.

Change

a.b_code = someCode

To

a.b_code = { [weak self] in print(self?.b_value) }

Or possibly

a.b_code = { [weak self] in self?.someCode() }

The point is that you must assign to b_code an anonymous function (what you call a closure), not a method reference, because that's the only way you can have a capture list that lets you manage the memory of the captured reference to self to break the retain cycle.

Passing self and returning it in a closure without retain cycle

No, from what I can see, this will not result in a retain cycle.


For example, this will result in a retain cycle. When myFunction executes, the Foo instance will hold a strong reference to the completion block. In turn, the completion block holds a strong reference to the Foo instance. This will a retain cycle, and in this case you need to add a capture list.

class Foo<T> {
var completion: (T) -> ()

func myFunction(_ vc: T, completion: @escaping (T) -> ()) {
self.completion = completion // This will create a retain cycle
completion(vc)
}

func doSomething() {
myFunction(self) { vc in
print(vc)
}
}
}

Will this cause a retain cycle Swift?

This wouldn’t cause a retain cycle because it looks like a static method on a different type. So the getTextFileData method will temporarily retain the closure you are passing in until any asynchronous work is complete, and the closure will temporarily retain self. But when the work is complete and the closure is done executing, those temporary retains will expire and automatic memory management can clean up as appropriate.

The danger of a retain cycle is when you have a closure the that references / captures self, and self also retains the closure, for example. Like this:

class GameController {
var games: [Game]?
// self retains the closure as a property
let updateClosure:([Game], Bool)->() = {
games, success in
if success {
self.games = games // and the closure captures self. Each retains each other indefinitely, this is a retain cycle and neither this closure nor self will ever be deallocated
}
}

func load() {
NPWebService.getTextFileData(updateClosure)
}
}

So normally weak self or unowned self in closure capture lists are only needed:

  • if self or something retained by self will be retaining that closure (this is rarely the case when a closure is created locally and transiently at the call site to pass into a method as an argument)
  • if for non-retain cycle reasons, the desired behavior is to allow self to be deallocated before some asynchronous work is completed, and before the closure is invoked. In this case the closure shouldn’t strongly capture self and instead should weakly capture it and check if it still exists before making use of self when the closure is invoked

swift - retain cycle when calling inner function?

Do I have a retain cycle

Yes, in the sense that you probably mean it. self (the SomeClass) is retaining the Manager, and the Manager, once doSomething has been called, is retaining the callback method progressComplete which refers to self.

So, it's not a permanent situation (a true leak), but SomeClass cannot go out of existence until such time as the Manager's requestData has performed its callback. It is easy to confirm this experimentally, and you can see it in the memory graph:

Sample Image

That's not necessarily a bad thing! But if it's going to be a problem, then you need to fix it.

However, note that weak self won't help you here, as there is no place to say it. You can only say weak self in the body of an anonymous function — and you don't have any anonymous functions!

So the correct way to write this, if you are concerned about it, would be to abandon your separate progressComplete function and write the callback as an anonymous function, and do the weak–strong dance, like this:

    self.manager.requestData { [weak self] success in
if let self = self {
self.someVar = nil
completion?(success)
}
}


Related Topics



Leave a reply



Submit