I've read several discussion on StackOverflow about when we should use [weak self]
or [unowned self]
in closures.
However, are there any cases when we don't have to use neither of them since Swift doesn't show any error or warning when we're just explicitly using self
inside a closure.
For example, should we use weak
or unowned
here?
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0.0
}
You need to use [weak self] or [unowned self] if your closure can cause a Strong Reference Cycle.
This can occur if you assign the closure to a property of self and you refer to self or a property of self inside the closure itself. Closures are reference types, hence essentially the same rules apply to strong references as if you used normal classes.
As for your example, there is no need for [weak self]
or [unowned self]
, since you don't assign the closure to a variable inside the class to which self
refers to, so there won't be a strong reference cycle.
For more information, check out the Strong Reference Cycles for Closures part of the Swift programming language guide. Here is an example from the mentioned link of when a strong reference cycle can be caused by closures:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
Without [unowned self]
in the closure of asHTML
, a strong reference cycle will be caused by the closure assigned to asHTML
. Changing the implementation of asHTML
to the following solves this issue:
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
This is somewhat opinion based, so I'll give my opinion :)
I generally base it on synchronicity. If a closure is Async, the calling instance may no longer exist when the closer is called and therefore [weak self]
should be used. If a closure is synchronous, it's unnecessary and capturing a strong reference is fine.
This could be expanded to also include closures where you can reasonably expect your instance to remain valid when it's called (e.g. your View animation case), however be aware this makes an assumption that the closure and your usage of it will remain unchanged, so it could theoretically break at some point in the future. This less safe and makes future maintenance more difficult/dangerous.
In the case of an established and predictable API like UIView.animate, I personally tend to use strong self for the sake of brevity, but that's an evaluation you'll need to do yourself and it depends on the usage.
Also as noted in the comments, this is true of function-closures. Assigning a closure to another variable's property has a different set of issues.
As an aside, I have adopted the approach of weak-reference closures simply calling another method in my Type, e.g.
thing.doSomethingWithAClosure() { [weak self]
self?.doSomething()
}
It simplifies the logic while also enforcing more functional/modular code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With