My question is more like better practice for the answer.
Say we have multiple nested layers of callbacks, each layer we have to make self
to be weak
and I know we can write guard
for each layer (see code snippet 1), but is this even necessary? If we only guard at the first layer should it be enough (see code snippet 2)?
If we think from the stand point of reference counting, will the first strongself
be good enough?
Snippet 1:
let callBack1 = { [weak self] xx in
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack2 = { [weak self] yy in {
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack3 = { [weak self] zz in
guard let strongSelf = self { return }
// strongSelf.func(param)
}
}
}
Snippet 2:
let callBack1 = { [weak self] xx in
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack2 = { [weak self] yy in {
// strongSelf.func(param)
let callBack3 = { [weak self] zz in
// strongSelf.func(param)
}
}
}
Note: This is a legit case in our code base, don't assume this will never happen.
Edit: To clarify the question, we assume each callback is happening asynchronously, and the self
here is reference the current class (maybe a model, maybe a view controller) that can be release / pop during the any of the three call backs happens.
With everything you have learned so far, you can finally understand what [weak self] means when working with closures in Swift. In Swift, [weak self] prevents closures from causing memory leaks in your application. This is because when you use [weak self], you tell the compiler to create a weak reference to self.
For that reason, weak references must be optional. When the referenced object goes away, the compiler sets any weak reference pointing to it to nil. There is a particular type of reference cycle, very common in Swift code, that happens in escaping closures.
As you can see, in that closure, we have a reference to self. The Swift compiler forces you to make explicit any self reference in a closure so that it’s clear that capturing is happening. The TimeTracker keeps a reference to the Timer in its timer property. The Timer keeps a reference to the closure to execute it every time it fires.
But if you want to keep all code in the closure, there is an alternative. The guard statement unwraps the self reference and assigns it to a let constant with the same name (in Swift, you can reuse some keywords as variable names using backticks). The following code then uses that constant, looking the same but without the unwrapping.
I think most part in @Andriy's answer is correct. But the most correct answer is that we don't even need to put [weak self]
in any nested blocks.
When I first heard this from my colleagues, I don't want to buy it. But the truth is that, the first block defines the captured self
to be weak
and this captured self will be affecting all the captured self within current scope, in other words, within first block's {}
. Therefore, there is no need to apply [weak self]
any more if we have done it in the first most layer of callback.
It is very hard to find the exact document from Apple to prove this but the following code snippet with core foundation retain count CFGetRetainCount()
it can prove that's true.
class TestClass {
var name = ""
var block1: (()->Void)?
var block2: (()->Void)?
func test() {
print(CFGetRetainCount(self))
self.block1 = { [weak self] in
self?.block2 = { // [weak self] in
print(CFGetRetainCount(self))
}
self?.block2?()
print(CFGetRetainCount(self))
}
self.block1?()
print(CFGetRetainCount(self))
}
deinit {
print(CFGetRetainCount(self))
}
}
do {
let tstClass = TestClass()
print(CFGetRetainCount(tstClass))
tstClass.test()
print(CFGetRetainCount(tstClass))
}
If you are interested, you can try this in your playground, feel free to remove the comment for the [weak self]
for block2, you will see the same answer.
Retain count is always 2 and deinit
is called correctly.
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