class A {
var b: B
init(b: B) {
self.b = b
}
deinit {
print(" Destroying A")
}
}
class B {
weak var a: A?
deinit {
print(" Destroying B")
}
}
func setup(_ a: inout A?, _ b: inout B?) {
b = B()
a = A(b: b!)
b?.a = a
}
var bravo: B?
var alpha: A?
setup(&alpha, &bravo)
bravo = nil
alpha = nil
// OUTPUT:
// " Destroying A"
// " Destroying B"
I've tried all permutations of setting alpha
and bravo
to nil, and yet I cannot get bravo
to deinit before alpha
. Based on my brief experiment in Swift Playgrounds, alpha
always gets deinit before bravo
. Can someone please explain why?
I know this has to do with ARC, but I thought if bravo = nil
, then isn't the value of alpha.b
also nil? If so, then wouldn't it be safe to deinit bravo
safely before alpha
?
I'd love to know what the retain counts are on each of these instances over time.
Not sure I understood the question, but I see this as quite straight forward application of ARC rules. After the line
setup(&alpha, &bravo)
you have
bravo
(the bravo
itself, and its reference alpha.b
)alpha
itself), and 1 weak (bravo.a
) reference to the object alpha
Now you set bravo = nil
, removing 1 strong reference, but the other strong reference remains, so deinit
is not called, based on rule:
ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
Next you set alpha = nil
. That's deleting its only strong reference, hence based on rule
A weak reference is a reference that doesn’t keep a strong hold on the instance it refers to, and so doesn’t stop ARC from disposing of the referenced instance.
A can be deallocated immediately. Also important to remember that
ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated
In other words, the order of operations is:
alpha = nil
nil
deinit
is calledSo now, that alpha
is deleted, no references of any kind are left for bravo
, and hence its deinit
can be called.