Is there a reason why `didSet` not being called on a weak property that becomes nil?

Extending the example from the other thread a little:

class Ref {
  weak var obj: Obj? {
    didSet { print(obj == nil ? "is nil" : "is not nil") }
  }
}

class Obj {
  weak var ref: Ref?

  func unlink(from ref: Ref) {
    print(ref.obj as Any)
  }

  deinit {
    print("deinit")
    ref.map(unlink(from:))
  }
}

var obj: Obj? = Obj()
let ref = Ref()
obj?.ref = ref
ref.obj = obj
obj = nil

The result is:

is not nil
deinit
nil

Why don't we track and fire the didSet observable when the object becomes nil? If there is a technical reason, so be it. However if not then it seems logical to me that the output should look like this:

is not nil
is nil
deinit
nil

If that were the case, that would mean releasing any class could have arbitrary side effects beyond what's in the deinit, because anyone could have made a weak variable referencing it. That's something we try to avoid in Swift.

Beyond that, pure Swift weak properties don't even get set to nil immediately; they do it the next time you load from the property. (This is different from Objective-C weak references.) So there's also not even a place to put a call to the setter today.

6 Likes