How to Correctly handle Weak Self in Swift Blocks with Arguments

IosSwiftRetain Cycle

Ios Problem Overview


In my TextViewTableViewCell, I have a variable to keep track of a block and a configure method where the block is passed in and assigned.
Here is my TextViewTableViewCell class:

//
//  TextViewTableViewCell.swift
//

import UIKit

class TextViewTableViewCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet var textView : UITextView

    var onTextViewEditClosure : ((text : String) -> Void)?
    
    func configure(#text: String?, onTextEdit : ((text : String) -> Void)) {
        onTextViewEditClosure = onTextEdit
        textView.delegate = self
        textView.text = text
    }
    
    // #pragma mark - Text View Delegate
    
    func textViewDidEndEditing(textView: UITextView!) {
        if onTextViewEditClosure {
            onTextViewEditClosure!(text: textView.text)
        }
    }
}

When I use the configure method in my cellForRowAtIndexPath method, how do I properly use weak self in the block that I pass in.
Here is what I have without the weak self:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {(text: String) in
   // THIS SELF NEEDS TO BE WEAK  
   self.body = text
})
cell = bodyCell

UPDATE: I got the following to work using [weak self]:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {[weak self] (text: String) in
        if let strongSelf = self {
             strongSelf.body = text
        }
})
cell = myCell

When I do [unowned self] instead of [weak self] and take out the if statement, the app crashes. Any ideas on how this should work with [unowned self]?

Ios Solutions


Solution 1 - Ios

If self could be nil in the closure use [weak self].

If self will never be nil in the closure use [unowned self].

If it's crashing when you use [unowned self] I would guess that self is nil at some point in that closure, which is why you had to go with [weak self] instead.

I really liked the whole section from the manual on using strong, weak, and unowned in closures:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

Note: I used the term closure instead of block which is the newer Swift term:

https://stackoverflow.com/questions/26374792/difference-between-block-objective-c-and-closure-swift-in-ios

Solution 2 - Ios

**EDITED for Swift 4.2:

As @Koen commented, swift 4.2 allows:

guard let self = self else {
   return // Could not get a strong reference for self :`(
}

// Now self is a strong reference
self.doSomething()

P.S.: Since I am having some up-votes, I would like to recommend the reading about escaping closures.

EDITED: As @tim-vermeulen has commented, Chris Lattner said on Fri Jan 22 19:51:29 CST 2016, this trick should not be used on self, so please don't use it. Check the non escaping closures info and the capture list answer from @gbk.**

For those who use [weak self] in capture list, note that self could be nil, so the first thing I do is check that with a guard statement

guard let `self` = self else {
   return
}
self.doSomething()

If you are wondering what the quote marks are around self is a pro trick to use self inside the closure without needing to change the name to this, weakSelf or whatever.

Solution 3 - Ios

Put [unowned self] before (text: String)... in your closure. This is called a capture list and places ownership instructions on symbols captured in the closure.

Solution 4 - Ios

EDIT: Reference to an updated solution by LightMan

See LightMan's solution. Until now I was using:

input.action = { [weak self] value in
    guard let this = self else { return }
    this.someCall(value) // 'this' isn't nil
}

Or:

input.action = { [weak self] value in
    self?.someCall(value) // call is done if self isn't nil
}

Usually you don't need to specify the parameter type if it's inferred.

You can omit the parameter altogether if there is none or if you refer to it as $0 in the closure:

input.action = { [weak self] in
    self?.someCall($0) // call is done if self isn't nil
}

Just for completeness; if you're passing the closure to a function and the parameter is not @escaping, you don't need a weak self:

[1,2,3,4,5].forEach { self.someCall($0) }

Solution 5 - Ios

Use Capture list

> Defining a Capture List > > Each item in a capture list is a pairing of the weak or unowned > keyword with a reference to a class instance (such as self) or a > variable initialized with some value (such as delegate = > self.delegate!). These pairings are written within a pair of square > braces, separated by commas. > > Place the capture list before a closure’s parameter list and return > type if they are provided:

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here 
} 

> If a closure does not specify a parameter list or return type because > they can be inferred from > context, place the capture list at the very start of the closure, > followed by the in keyword:

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}

additional explanations

Solution 6 - Ios

As of swift 4.2  we can do:
_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()

Solution 7 - Ios

Swift 4.2

let closure = { [weak self] (_ parameter:Int) in
    guard let self = self else { return }

    self.method(parameter)
}

https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md

Solution 8 - Ios

You can use [weak self] or [unowned self] in the capture list prior to your parameters of the block. The capture list is optional syntax.

[unowned self] works good here because the cell will never be nil. Otherwise you can use [weak self]

Solution 9 - Ios

From Swift 5.3, you do not have to unwrap self in closure if you pass [self] before in in closure.

Refer someFunctionWithEscapingClosure { [self] in x = 100 } in this swift doc

Solution 10 - Ios

If you are crashing than you probably need [weak self]

My guess is that the block you are creating is somehow still wired up.

Create a prepareForReuse and try clearing the onTextViewEditClosure block inside that.

func prepareForResuse() {
   onTextViewEditClosure = nil
   textView.delegate = nil
}

See if that prevents the crash. (It's just a guess).

Solution 11 - Ios

[Closure and strong reference cycles]

As you know Swift's closure can capture the instance. It means that you are able to use self inside a closure. Especially escaping closure[About] can create a strong reference cycle[About]. By the way you have to explicitly use self inside escaping closure.

Swift closure has Capture List feature which allows you to avoid such situation and break a reference cycle because do not have a strong reference to captured instance. Capture List element is a pair of weak/unowned and a reference to class or variable.

For example

class A {
    private var completionHandler: (() -> Void)!
    private var completionHandler2: ((String) -> Bool)!
    
    func nonescapingClosure(completionHandler: () -> Void) {
        print("Hello World")
    }
    
    func escapingClosure(completionHandler: @escaping () -> Void) {
        self.completionHandler = completionHandler
    }
    
    func escapingClosureWithPArameter(completionHandler: @escaping (String) -> Bool) {
        self.completionHandler2 = completionHandler
    }
}

class B {
    var variable = "Var"
    
    func foo() {
        let a = A()
        
        //nonescapingClosure
        a.nonescapingClosure {
            variable = "nonescapingClosure"
        }
        
        //escapingClosure
        //strong reference cycle
        a.escapingClosure {
            self.variable = "escapingClosure"
        }
        
        //Capture List - [weak self]
        a.escapingClosure {[weak self] in
            self?.variable = "escapingClosure"
        }
        
        //Capture List - [unowned self]
        a.escapingClosure {[unowned self] in
            self.variable = "escapingClosure"
        }
        
        //escapingClosureWithPArameter
        a.escapingClosureWithPArameter { [weak self] (str) -> Bool in
            self?.variable = "escapingClosureWithPArameter"
            return true
        }
    }
}
  • weak - more preferable, use it when it is possible
  • unowned - use it when you are sure that lifetime of instance owner is bigger than closure

[weak vs unowned]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNatashaTheRobotView Question on Stackoverflow
Solution 1 - IosTenaciousJayView Answer on Stackoverflow
Solution 2 - IosLightManView Answer on Stackoverflow
Solution 3 - IosikuramediaView Answer on Stackoverflow
Solution 4 - IosFerran MaylinchView Answer on Stackoverflow
Solution 5 - IoshbkView Answer on Stackoverflow
Solution 6 - IosSentry.coView Answer on Stackoverflow
Solution 7 - IosAlan McCoshView Answer on Stackoverflow
Solution 8 - IosRufusView Answer on Stackoverflow
Solution 9 - IosDeepak ThakurView Answer on Stackoverflow
Solution 10 - IosMichael GrayView Answer on Stackoverflow
Solution 11 - IosyoAlex5View Answer on Stackoverflow