I'm experimenting the retain cycle in closure like following
class Sample {
deinit {
print("Destroying Sample")
}
func additionOf(a: Int, b:Int) {
print("Addition is \(a + b)")
}
func closure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
self?.additionOf(3, b: 3)
usleep(500)
self?.additionOf(3, b: 5)
}
}
}
Later at some point, I'm doing
var sample : Sample? = Sample()
sample?.closure()
dispatch_async(dispatch_get_global_queue(0, 0)) {
usleep(100)
sample = nil
}
The output will be
Addition is 6
Destroying Sample
This is understandable because self
is nil out before doing self?.additionOf(3, b:5)
If I made a change inside the closure by creating another variable which references to [weak self]
like following
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.additionOf(3, b: 3)
usleep(500)
strongSelf.additionOf(3, b: 5)
}
This time, the output is
Addition is 6
Addition is 8
Destroying C
My question is why strongSelf
is not nil out after sample = nil
. Is it because it is captured inside the closure before sample = nil
Let's consider your second example:
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.additionOf(3, b: 3)
usleep(500)
strongSelf.additionOf(3, b: 5)
}
This is a common pattern and it effectively says "if self
has been deallocated, return
immediately, otherwise establish a strong reference, strongSelf
, and maintain this strong reference until the closure finishes."
So, in your example, self
was not nil
when this dispatched block started, so as soon as we assigned strongSelf
to refer to it, we now have two strong references to this Sample
object, both the original sample
reference and this new strongSelf
reference inside this closure.
So, when your other thread removes its own strong reference, sample
, to this Sample
object (either by falling out of scope or by explicitly setting sample
to nil
), this strongSelf
strong reference is still present and will keep the object from being deallocated (or at least not until this dispatched block finishes).
In your comment above, you ask:
I still not understand why
strongSelf
does not get changed since self isnil
out...
Strong references never get set to nil
just because some other strong reference (even if it was the original strong reference) was set to nil
. That behavior of a reference getting set to nil
only applies to weak
references, not to strong references.
When your code performs sample = nil
on the first thread, all that does is remove a strong reference. It doesn't delete the object or anything like that. It just removes its strong reference. And now that the dispatched block has its own strong reference, all that happens upon sample = nil
is that the object that had two strong references now only has one strong reference left (the strongSelf
reference in the dispatched block). Only when this final strong reference is removed will the object be deallocated and deinit
will be called.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.