Nested callback strong reference cycle - ios

#IBAction func sendSweet(sender: UIBarButtonItem) {
var inputTextField: UITextField?
let alert = UIAlertController(title: "New sweet", message: "Enter a sweet", preferredStyle: .alert)
alert.addTextField { (textField: UITextField) in
textField.placeholder = "Your sweet"
inputTextField = textField
}
let sendAction = UIAlertAction(title: "Send", style: .default, handler: {
[weak self] (alertAction: UIAlertAction) in
guard let strongSelf = self else { return }
if inputTextField?.text != "" {
let newSweet = CKRecord(recordType: "Sweet")
newSweet["content"] = inputTextField?.text as CKRecordValue?
let publicData = CKContainer.default().publicCloudDatabase
publicData.save(newSweet, completionHandler: {
(record: CKRecord?, error: Error?) in
if error == nil {
// we want ui code to dispatch asychronously in main thread
DispatchQueue.main.async {
strongSelf.tableView.beginUpdates()
strongSelf.sweets.insert(newSweet, at: 0)
let indexPath = IndexPath(row: 0, section: 0)
strongSelf.tableView.insertRows(at: [indexPath], with: .top)
strongSelf.tableView.endUpdates()
}
} else {
if let error = error {
print(error.localizedDescription)
return
}
}
})
}
})
alert.addAction(sendAction)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
I have this callback hell, I want to know
Does the [weak self] & guard let strongSelf at very top of the callback hell prevent the strong reference cycle all out through the GCD's async callback. I read some other post at here, also one from a book, that said if the object I refer inside a callback can deinit successfully, it means a good sign for not having strong reference cycle, is it still true?
How to prevent this kind of callback hell, can you lead me to some reading material or topic I have missed? anything like javascript's promise chaining syntax?

As far as I can see there is no retain cycle and thus there is no need to weakify self. You certainly can do it in every block to be defensive.
There is no retain cycle because the instance (self) does not have reference to any of the closures. Especially to sendAction, as sendAction is declared inside of the sendSweet function.
class MyView: UIView {
let str = "some variable to have somsthing to use self with"
func foo() {
let ba = {
// no problem. The instance of MyView (self) does not hold a (strong) reference to ba
self.str.trimmingCharacters(in: CharacterSet.alphanumerics)
}
ba()
}
}
If you would move let sendAction = ... outside of the function as a property of the instance, you would have a reference cycle though. In this case the instance (self) would have a strong refrence to sendAction and the sendAction closure would have a strong reference to the instance (self):
self <-> { self. ...} aka sendAction.
class MyView: UIView {
let str = "asd"
// Problem.
// The instance of MyView (self) does hold a (strong) reference to ba ...
let ba: () -> Void
override init(frame: CGRect) {
super.init(frame: frame)
ba = {
// ... while ba holds a strong reference to the instance (self)
self.str.trimmingCharacters(in: CharacterSet.alphanumerics)
}
}
func foo() {
ba()
}
}
In this case you have to break the cycle by weakifying self within the closure, like you did.
How to prevent this kind of callback hell, can you lead me to some reading material
Checkout DispatchGroups.
(Apple Documentation)

Found a pretty neat solution to my problem #2
https://github.com/duemunk/Async
Example snippet:
Async.userInitiated {
return 10
}.background {
return "Score: \($0)"
}.main {
label.text = $0
}

Related

Am I capturing self in this nested function? The compiler does not fire a warning

I can't find any official documentation on this and there's mixed opinions out there.
In the following situation, all is well.
final class MyVC: UIViewController {
var space: Space!
private let tableView = MenuCategoriesTableView()
private let tableViewHandler = MenuCategoriesTableViewHandler()
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.dataSource = tableViewHandler
tableView.delegate = tableViewHandler
tableViewHandler.didSelectRow = { [unowned self] option in
let category = option.makeCategory()
if category.items.count > 0 {
let controller = MenuItemsViewController()
controller.title = option.rawValue
controller.space = self.space
self.show(controller, sender: self)
} else {
// whatever
}
}
}
}
However, if I make the following change, I no longer need to use unowned self, but I'm still concerned about capture self. Should I be concerned? If not, why?
final class MyVC: UIViewController {
...etc...
override func viewDidLoad() {
super.viewDidLoad()
...etc...
func categorySelected(_ option: MenuOption, _ category: MenuCategory) {
let controller = MenuItemsViewController()
controller.title = option.rawValue
controller.space = space
show(controller, sender: self)
}
tableViewHandler.didSelectRow = { option in
let category = option.makeCategory()
if category.items.count > 0 {
categorySelected(option, category)
} else {
// whatever
}
}
}
}
When you assign a closure to tableViewHandler.didSelectRow, you assign to it and retain whatever that closure captures.
self is retaining tableViewHandler.
Therefore, the danger is that you will refer to self within the closure. If you do, that's a retain cycle.
And this might not be due to referring to self explicitly. Any mention of a property or method of self is an implicit reference to self.
Okay, so with that out of the way, let's examine the closure.
You do not mention self implicitly or explicitly in the body of the closure. However, you do call a local method, categorySelected. Therefore, you capture this method.
And categorySelected does mention self. Therefore, it captures self (because every function is a closure).
Thus there is a potential retain cycle and you should continue to say unowned self to prevent a retain cycle.
(I presume that the compiler can't help you here by warning of the retain cycle; there's too much complexity. It's a problem you just have to solve by human reason.)
I did some investigations, and indeed you get a retain cycle if you use an inner function that references self.
Here is an example:
typealias ClosureType = () -> ()
class Test {
var closure:ClosureType?
let value = 42
func setClosure1() {
self.closure = {
print ("from setClosure1")
}
}
func setClosure2() {
self.closure = {
[unowned self] in
let v = self.value
print ("from setClosure2 - value: \(v)")
}
}
func setClosure3() {
func innerFunc() {
// [unowned self] in // not allowed, compile error (sometimes even crashes)
let v = value
print ("value: \(v)")
}
self.closure = {
[unowned self] in // compiler warning: "Capture [self] was never used"
print ("from setClosure3")
innerFunc()
}
}
deinit {
print ("Deinit")
}
}
If you use setClosure1 (trivial) or setClosure2 (capture clause), no retain cycle occurs:
if (1==1) {
let t = Test()
t.setClosure1()
t.closure?()
} // deinit called here
but if you call setClosure3, deinit will not be called:
if (1==1) {
let t = Test()
t.setClosure3()
t.closure?()
} // deinit NOT called here
There is no direct way to solve this problem; as you can see, using [unowned self] in the inner function results in an compiler error, and using it in setClosure3 results in a warning.
Nevertheless, there is a way to get around this issue - instead of using an inner function, you can use a second closure, in which you can specify the [unowned self] capture clause:
func setClosure4() {
let innerClosure:ClosureType = {
[unowned self] in
let v = self.value
print ("value: \(v)")
}
self.closure = {
print ("from setClosure4")
innerClosure()
}
}
// ...
if (1==1) {
let t = Test()
t.setClosure4()
t.closure?()
} // deinit called here
Conclusion: Swift should allow capture clauses in innner functions.

Should we continue explicitly capturing variable as weak in iOS

Suppose we got a chain of closures, like so:
var myOtherVc: UIViewController! // get it somehow
self.dismiss(animated: true, completion: { [weak myOtherVc] in
myOtherVc?.present(sthElse, animated: true, completion: { [weak myOtherVc] in // <-- HERE
})
})
My question is if we captured variable myOtherVc in the topmost block as weak should we keep being explicit about weak in all the children blocks or is compiler smart enough to tell ARC to not retain?
Update
I guess I need to clarify, what if the block was escaping?
Also, i DO care about delayed deallocation. This is the whole point of using weak for me.
public func doWhatever(_ success: #escaping () -> Void) {
// do whatever
})
var myOtherVc: UIViewController! // get it somehow
self.dismiss(animated: true, completion: { [weak myOtherVc] in
SomeClass.doWhatever({ [weak myOtherVc] in // <-- HERE
myOtherVc?.present(sthElse, animated: true, completion: { [weak myOtherVc] in // <-- and HERE, too
})
})
})
We suppose all closures are escaping, so I made this playground and I conclude that you must capture your variable as weak in the latest closure that is using your variable and it won't infer its reference type from the parent or top closure:
typealias Closure = () -> Void
class A {
var closureA : Closure?
func runClosureA(closure: #escaping Closure) {
self.closureA = closure
closureA?()
}
func print() {
debugPrint("A is here!")
}
deinit {
debugPrint("A deinited")
}
}
another class which operates on chained closure:
class B {
func runClosureB(closure: #escaping Closure) {
closure()
}
func operate() {
let a : A = A()
runClosureB { [weak a] in
a?.runClosureA { [a] in
a?.print()
}
}
}
deinit {
debugPrint("B deinited")
}
}
The code is:
var b: B? = B()
b?.operate()
b = nil
It will prints:
// "A is here!"
// "B deinited"
But by changing the operate function to this:
func operate() {
let a : A = A()
runClosureB { [a] in
a.runClosureA { [weak a] in
a?.print()
}
}
}
The result will change to this:
"A is here!"
"A deinited"
"B deinited"
Update: In class A I made a strong reference to the closure as closureA, if you don't create the reference, there is no need to capture self as weak in closures.
In fact, it depends on which closure you are using and the relations between them and if there can be retained cycle so you should consider capturing the right closure as weak.
In your case, with present(_, animated:, completion:), the completion block is non-escaping so if you want to use weak reference you can use it but it is not necessary to use.
Non-escaping closures do not require [weak self] unless you care about delayed deallocation
Please check the article about weak, unowned references in nested closures.
I made a small test in the playground that shows me that the compiler is indeed quite smart and tells ARC not to retain.
class WeakThingy {
var variable: Int
init(variable: Int) {
self.variable = variable
}
deinit {
print("deinit WeakThingy")
}
}
class ClosureTest {
var maybeNil: WeakThingy
init(maybeNil: WeakThingy) {
self.maybeNil = maybeNil
}
deinit {
print("deinit ClosureTest")
}
func bien() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak maybeNil] in
print("first \(String(describing: maybeNil))")
maybeNil?.variable = 12
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
print("second \(String(describing: maybeNil))")
maybeNil?.variable = 12
}
}
}
}
var closureTest:ClosureTest? = ClosureTest(maybeNil: WeakThingy(variable: 12))
closureTest?.bien()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
closureTest = nil
}
What's printed is the following
first Optional(__lldb_expr_34.WeakThingy)
deinit ClosureTest
deinit WeakThingy
second nil
What happen here is that I pass maybeNil in the first closure as weak, but not in the second. Then I make it nil before the second closure gets executed. We can see in the output that it is well deallocated before entering the second closure.

How can I make a variable inside a function global?

I currently have the following function called saveRun()
func saveRun() {
let startLoc = locations[0]
let endLoc = locations[locations.count - 1]
let startLat = startLoc.coordinate.latitude
let startLong = startLoc.coordinate.longitude
let endLat = endLoc.coordinate.latitude
let endLong = endLoc.coordinate.longitude
//1. Create the alert controller
let alert = UIAlertController(title: "Save the Run", message: "Choose a name: ", preferredStyle: .alert)
//2. Add the text field
alert.addTextField { (textField) in
textField.text = ""
}
// 3. Grab the value from the text field, and print it when the user clicks OK
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
let textField = alert?.textFields![0] // Force unwrapping because we know it exists.
// Create name for run
let runName = textField?.text
let run = self.databaseRef.child(runName!)
let user = FIRAuth.auth()?.currentUser?.uid
// Enter run info into db
run.child("startLat").setValue(startLat)
run.child("startLong").setValue(startLong)
run.child("endLat").setValue(endLat)
run.child("endLong").setValue(endLong)
run.child("distance").setValue(self.distance)
run.child("time").setValue(self.seconds)
run.child("user").setValue(user)
// Enter locations into db
var i = 0
for location in self.locations {
run.child("locations").child("\(i)").child("lat").setValue(location.coordinate.latitude)
run.child("locations").child("\(i)").child("long").setValue(location.coordinate.longitude)
i = i + 1
self.performSegue(withIdentifier: DetailSegueName, sender: nil)
}
}))
// 4. Present the alert
self.present(alert, animated: true, completion: nil)
}
My problem is that I am trying to extract 'runName' from the action that I am adding when the user clicks 'Ok' on the alert controller and using it in the following function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detailViewController = segue.destination as? DetailViewController {
detailViewController.runName = self.runName
}
}
When I try to print 'runName' in DetailViewController, the value of runName is nil. The issue I think is that I cannot set a global variable inside the action I have added as it is in a function. Is there any other way I can obtain this variable's value and use it outside of the function?
Class YourClassName:UIViewController {
var runName:String = "" // This will be global for your class
//Remove local decalration of runName variable
func saveRun() { // your function
alert.addAction(
//.....
self.runName = textfield?.text
)
}
}
Now you can use in whole class.
I solved this thanks to #DSDharma pointing out that even if 'runName' was set as a global variable, using it as a global variable inside of an alert function block required the 'self' keyword.
For example, before I had the following inside of the alert function block:
let runName = textField?.text
This needed to be changed to:
self.runName = textField?.text

Swift - Function as Variable with Self Reference

I've made a struct to hold data for my UITableViewCells that looks like so:
struct CellData {
var title: String
var action: () -> Void
init(title: String, action: () -> Void) {
self.title = title
self.action = action
}
}
And in my TableViewController, I'm setting it up like this:
lazy var buttonCells: [CellData] = [
CellData(
title: "Button 1",
action: {
**self.doSomething()**
}
),
CellData(
title: "Button 2",
action: {
**self.doSomethingElse()**
}
)
]
However, the use of self inside the action causes it so that the TableViewController isn't deinitialized when the controller is dismissed (the deinit function isn't called). When I remove the self references, and replace it with something else, the TableViewController is deinitialized just fine. How can I go about fixing this abandoned memory issue?
Change your action definitions to use a capture group:
action: {
[weak self] in
guard let strongSelf = self else {
return
}
strongSelf.doSomething()
}
What the [weak self] capture group declaration does is to convert self to a weak variable inside the block. If the owning object gets deallocated while the block is waiting to be called it gets passed nil instead.
Then once inside the block the guard statement tries to map the weak self definition to a strong local variable. If self is nil, it exits. If not, strongSelf contains an unwrapped strong reference to self and you proceed as normal.
Strong Reference Cycle
Inside your table view controller you are probably creating CellData like this right?
CellData(title: "Hello") {
// here you are using self
}
Or maybe you are writing this
CellData(title: "Hello", action: {
// here you are using self
})
In both case there's a problem because you are creating a Strong Reference Cycle.
Solution
you can avoid that using this code
CellData(title: "Hello") { [unowned self] in
// here you can use self
}
Last thing
Just remove the init in your struct, Swift will provide it for you ;)
struct CellData {
let title: String
let action: () -> ()
}

How to Correctly handle Weak Self in Swift Blocks with Arguments

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]?
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:
Difference between block (Objective C) and closure (Swift) in 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.
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) }
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.
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
As of swift 4.2 🔸 we can do:
_ = { [weak self] value in
guard let self = self else { return }
print(self) //👈 will never be nil
}()
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
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]
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
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).
[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]

Resources