cellForRow we give the cell a closure to cause the ViewController to
The cell needs a variable to store the closure it gets handed.
You might’ve noticed Xcode complained.
This means that the closure handed to the cell will “capture” the ViewController. In other words, it will hold a strong reference to it. The ViewController is the TableViewDelegate for those cells, so it will also hold strong references to them. Since both maintain strong references to each other, their references are circular, causing a “retain cycle”.
Automatic Reference Counting (ARC) is a mechanism built into the Objective-C runtime that is responsible for memory management in iOS applications. ARC cleans up objects floating around in memory when nothing has a strong reference to them. ARC checks how many other objects reference some object, and if that count is zero it deallocates that object from memory.
Circular references cause counts to stay above zero so objects are never released from memory.
A Problematic Solution
We can force the
self reference to be
weak like this:
But we cannot do this:
… because you can’t use closure syntax for
[weak self] inside of a normal function.
You have to wrap the calling on
doSomething inside of a closure that you hand to the cell. So instead of passing the original function around, we have to wrap it inside a closure and pass the closure.
If that TableViewCell contains a CollectionView, it might want to hand the action to each of its cells. Each time the function gets passed around - to keep it from leaking memory - you’ll need to wrap each closure inside of another closure to keep the references weak.
So now doSomething() is wrapped inside of two closures: closure(closure(function)). Even worse, if you want to pass or receive variables from this function, it quickly becomes very hacky and complex, and therefore difficult to understand and maintain.
A Better Solution
If instead we could pass the original function doSomething(), then using it one, two, or ten levels deep would be the same as calling it directly from the top ViewController, but we need to make sure the references to that closure are always weak. Unfortunately, you can’t declare a closure variable with a
weak keyword like this:
So we need to force the “weak” reference ourselves. Simply set
closure = nil when the view dissapears. For example, in the ViewController:
Or any subclass of UIView:
willMoveTo(newWindow) == nil the view was removed from the view heirarchy. This seems like a good time to remove the reference and allow ARC to deallocate it. If this was a TableViewCell for example, when the ViewController dequeues the cell again it will be handed a new closure.
Simple and effective.