The script ceases to be "persistent" when you close the GUI, so it exits (apparently before releasing the object). If you use
Persistent, you will see the message.
There is a special case in the implementation of __New: when
EventObj == this, it avoids calling AddRef or Release, since they aren't needed and would only have a detrimental effect.
If you pass some other object as the Gui's event sink, it must AddRef before storing the reference (and Release when the Gui is destroyed), otherwise the object could be deleted prematurely, leaving the Gui with a dangling pointer.
Each control also has a pointer to its Gui, but not a counted reference. Holding a reference to a control will not prevent the Gui from being deleted. After the GUI is destroyed (which implicitly occurs when it is deleted, if not before), the control is no longer valid and will not allow you to retrieve
ctrl.Gui.
Code: Select all
g := Gui()
c := g.AddText()
g := ""
MsgBox type(c.gui) ; Error: The control is destroyed.
The lesson is that if you know when the object will be deleted or can control what happens when it is deleted, you can keep a pointer instead of a counted reference.
wpb wrote:If I were to add something like this to your example:
..then I'd still be introducing a circular reference with the anonymous function in the same way as you described before, wouldn't I?
Yes. There's no point specifying
EventObj if you're not going to use method names for your event handlers. You can also create circular references completely independent of the event handlers, such as by assigning properties.
EventObj will obviously not help in those cases.
I have added the following to the documentation.
The Gui retains a reference to EventObj for the purpose of calling event handlers, and releases it when the window is destroyed. If EventObj itself contains a reference to the Gui, this would typically create a circular reference which prevents the Gui from being automatically destroyed. However, an exception is made for when EventObj is the Gui itself, to avoid a circular reference in that case.
The point of the exception is to provide an alternative to the other methods of specifying event handlers which are prone to circular references. You need to actually use that
alternative as such, not in combination with the more problematic methods.
Come to think of it, the event handlers are also deleted when the window is destroyed. So although your last example does create a circular reference due to capturing
this, the circle is broken if you destroy the window. However, it may still be more efficient to avoid creating a closure; i.e.
(this, *) => this.LineLimit.Enabled := not this.LineLimit.Enabled. (Naming the parameter
this prevents you from accidentally capturing the outer one.)