I have a class with a weak reference to its delegate. In a background operation, I need to set the delegate, perform an operation on the class, and then have the delegate released.
The code below works in Debug mode, but fails in Release mode, because in Release mode the delegate is released right away.
protocol DocumentDelegate:class { ... }
class MyDocument {
weak var delegate:DocumentDelegate?
func save() {
assert(self.delegate =! nil)
}
}
// Later:
// (#1) Using "var" does not work:
var delegate:DocumentDelegate? = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
delegate = nil
// (#2) Using "let" does work:
let delegate:DocumentDelegate = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
I assumed that the last instruction delegate = nil
would cause the compiler to keep the delegate around until then (i.e. the "last" time the variable is used). However, thinking about it, it does make sense that the compiler optimizes the code and releases the delegate instance right away, since there are no other strong references.
However, I do not understand why the compiler does not behave the same way in the second case when using "let". Here as well the compiler could see that the delegate is not referenced via a strong reference anywhere else, but it does keep it around until the end of the block.
What would be a good way to think about this and what is a good way to keep a strong reference to the weak delegate?
While I wholeheartedly agree with Rob Napier’s analysis, for the sake of completeness, I should note that you can also make the lifetime of the object explicit:
let delegate = InterimDelegate()
withExtendedLifetime(delegate) {
let document = MyDocument()
document.delegate = delegate
document.save()
}