Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Closures - Capturing self as weak

I am trying to resolve a closure based strong reference cycle in Swift.
In the code below, object is retained by the owning view controller. ProgressHUD is a UIView that's also retained by the owning view controller. ProgressHUD is leaked every time the completion handler is called. When using the new closure capture feature, declaring self as weak or unowned does not resolve the memory leak.

object.setCompletionHandler { [weak self] (error) -> Void in
    if(!error){
        self?.tableView.reloadData()
    }
    self?.progressHUD?.hide(false)
}

However, if I declare a weak var for self outside of the closure, it fixes the memory leak, like this:

weak var weakSelf = self
object.setCompletionHandler { (error) -> Void in
    if(!error){
        weakSelf?.tableView.reloadData()
    }
    weakSelf?.progressHUD?.hide(false)
}

Any ideas as to why this is not working with Swift capturing?

like image 450
Fergal Rooney Avatar asked Sep 22 '14 13:09

Fergal Rooney


People also ask

Should we always use weak self in closure?

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.

Does closure capture values in Swift?

A closure can capture constants and variables from the surrounding context in which it's defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

How can you avoid strong reference cycles in a closure Swift?

A strong reference cycle happens when 2 instances keep a strong reference to each other. You can accidentally create such a cyclic reference, for example when working with 2-way “links” between objects, or with closures. You can break the cycle by marking a reference as weak, or by setting one of the references to nil.

What is Self in closure?

As the name implies, a self-executing closure is a closure that is immediately executed. Some developers use the term immediately-executing closure for that reason. Let's append a pair of parentheses to the closure to execute it.


2 Answers

You stated that progressHUD is retained by the owning view controller (self) and you reference it in your closure...so add it to the capture list and then use the captured variable in the closure as follows:

object.setCompletionHandler { [weak self] (error) -> Void in
    if(!error){
        self?.tableView.reloadData()
    }
    self?.progressHUD.hide(false)
}
like image 85
dferrero Avatar answered Nov 07 '22 21:11

dferrero


If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles. source Apple

source sketchyTech First, it is important to make clear that this whole issue only concerns closures where we are assigning "a closure to a property of a class instance". Keep this in mind with each rule. The rules:

  1. use weak capture if the class instance or property is an optional
  2. use unowned if the class instance or property is non-optional and can never be set to nil
  3. "you must ... use the in keyword, even if you omit the parameter names, parameter types, and return type"

In answear to your question there should be no retain cycle.

like image 26
Shial Avatar answered Nov 07 '22 22:11

Shial