Home > Blockchain >  If class A contains a strong reference to class B and class B has a weak reference to class A, why d
If class A contains a strong reference to class B and class B has a weak reference to class A, why d

Time:09-22

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.

CodePudding user response:

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

  • 2 strong references to the object bravo (the bravo itself, and its reference alpha.b)
  • 1 strong (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:

  1. String reference is deleted with alpha = nil
  2. Since there are no other strong references, all weak references are set to nil
  3. deinit is called

So now, that alpha is deleted, no references of any kind are left for bravo, and hence its deinit can be called.

  • Related