Reputation:
I have this small Swift script, which uses weak references:
#!/usr/bin/env swift
class Thing
{
deinit
{
print("Thing object deallocated")
}
}
class WeakThing
{
weak var thing: Thing?
{
didSet
{
print("Set thing to \(thing)")
}
}
}
var thing = Thing()
let weakThing = WeakThing()
weakThing.thing = thing
thing = Thing()
print("weakThing's thing is \(weakThing.thing)")
This prints:
Set thing to Optional(Test.Thing)
Thing object deallocated
weakThing's thing is nil
However, I would expect it to print:
Set thing to Optional(Test.Thing)
Set thing to nil
Thing object deallocated
weakThing's thing is nil
What am I doing incorrectly? I see that the object is being deallocated, and that the value of the thing
variable is changing, but my didSet
code is not executing.
Upvotes: 9
Views: 2289
Reputation: 241
I know this question is very old, but I stumbled across another answer that actually get's the problem solved here: https://stackoverflow.com/a/19344475/4069976
For what it's worth, this is my implementation to watch a deinit as suggested by the answer referenced above. Just make sure you don't create any retain cycles with your onDeinit closure!
private var key: UInt8 = 0
class WeakWatcher {
private var onDeinit: () -> ()
init(onDeinit: @escaping () -> ()) {
self.onDeinit = onDeinit
}
static func watch(_ obj: Any, onDeinit: @escaping () -> ()) {
watch(obj, key: &key, onDeinit: onDeinit)
}
static func watch(_ obj: Any, key: UnsafeRawPointer, onDeinit: @escaping () -> ()) {
objc_setAssociatedObject(obj, key, WeakWatcher(onDeinit: onDeinit), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
deinit {
self.onDeinit()
}
}
Call it like this when initializing your weak var:
self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })
Upvotes: 5
Reputation: 62062
didSet
and willSet
are not called when a weak-reference is auto-zeroed due to ARC.
If you were to manually set the property to nil
, you would see the didSet
code called.
Upvotes: 8