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]
?
This question is related to
ios
swift
retain-cycle
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) }
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:
Note: I used the term closure instead of block which is the newer Swift term:
Difference between block (Objective C) and closure (Swift) in 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.
_ = { [weak self] value in
guard let self = self else { return }
print(self) // will never be nil
}()
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
}
[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 possibleunowned
- use it when you are sure that lifetime of instance owner is bigger than closure**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.
Swift 4.2
let closure = { [weak self] (_ parameter:Int) in
guard let self = self else { return }
self.method(parameter)
}
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]
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).
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
Source: Stackoverflow.com