In order to have a retain cycle, you need to have a strong reference on each direction, i.e.:
Object A strongly references Object B
Object B strongly references Object A
Assuming self
in the code you shared is a View Controller, and assuming someView
is a strong reference to a view, we could say that:
Object A (View Controller) strongly references Object B (Some View)
Now if Object B (Some View) has a strong reference back to the View Controller, you will have a retain cycle.
Assuming doSomething
is a method in your ViewController, and not a closure, you will have a retain cycle
An easy way to check this, is by implementing deinit
in both your Some View and your View Controller, like so:
class SecondViewController: UIViewController {
var someView: CustomView?
override func viewDidLoad() {
super.viewDidLoad()
someView = CustomView(frame: view.frame)
someView?.someCallback = doSomething
}
func doSomething() {
}
deinit {
print(#function)
}
}
final class CustomView: UIView {
var someCallback: (() -> Void)?
deinit {
print(#function)
}
}
You will see that the print
s on deinit
are never printed out in the console. However changing the way you assign someCallback
to:
someView?.someCallback = { [weak self] in
self?.doSomething()
}
will cause deinit
to run, thus breaking the retain cycle
Edit:
Or even, as an alternative:
weak var weakSelf = self
someView?.someCallback = weakSelf?.doSomething
(Even though this is using a weak reference, because this expression is evaluated at the time the assignment of someCallback
is performed, not at the time it is executed, this will still become a strong
reference) - Thanks @Rob