I have a very simple form, that uses a very simple class to handle some things. And that class has a Class_Terminate
sub to clean up after itself. However, that doesn't seem to be firing when the form gets closed.
MCVE:
Form Form1, one text box named Text0, no further controls
Private myClass1 As Class1
Private Sub Form_Load()
Set myClass1 = New Class1
myClass1.InitForm Me
End Sub
Class Class1
Public theForm As Form
Private WithEvents SomeTextbox As TextBox
Public Sub InitForm(frm As Form)
Set theForm = frm
Set SomeTextbox = frm.Text0
End Sub
Private Sub Class_Terminate()
MsgBox "Class1 terminated succesfully"
End Sub
However, the class terminate handler doesn't fire when I close the form.
I tried unsetting the Form object in the class:
Private Sub Form_Unload(Cancel As Integer)
Set myClass1.theForm = Nothing
End Sub
But chaos ensued: The class terminate handler fires after closing the form, but immediately afterwards Access hard-crashes without any error message!
Access doesn't gracefully clean up the form object when you close a form.
That means: if an object has open references to a form, the form object persists. It only can get removed by the garbage collector if there are no references to it.
The first version of the form was creating a memory leak: the form object Form_Form1
had a reference to Class1
(through the MyClass1
variable), Class1
had a reference to the form object through the theForm
variable. This caused a reference loop. The terminate handler didn't fire because the class never terminated, it remained in memory indefinitely, and closing and reopening the form just opened up a new instance of the class.
The second version caused a problem: while the reference loop was broken, references to Form1
were released first (because there still was a reference to Class1
on Form1
), causing the garbage collector to clean that up, then references to Class1
were released and the garbage collector tried to clean up Class1
, including the textbox object SomeTextbox
, causing Access to hard-crash since the form object was already cleaned up and the textbox object was invalid.
The solution is to break the reference loop by removing all references to Class1
first. This doesn't cause crashes.
Private Sub Form_Unload(Cancel As Integer)
Set myClass1 = Nothing
End Sub
This causes the garbage collector to clean up the Class1 instance first, releasing references to Text0, then cleans up the form objects because no-one has open references to that.