Class_Terminate不在表单上触发对象

时间:2018-10-26 14:39:20

标签: vba ms-access access-vba

我有一个非常简单的表单,它使用一个非常简单的类来处理某些事情。该类有一个Class_Terminate子项可以自己清除。但是,关闭表单后似乎并没有触发。

MCVE:

Form 1,一个名为Text0的文本框,没有其他控件

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

但是,当我关闭表单时,类终止处理程序不会触发。

我尝试取消该类中的 Form 对象:

Private Sub Form_Unload(Cancel As Integer)
    Set myClass1.theForm = Nothing
End Sub

但是随之而来的是混乱:类终止处理程序在关闭表单后触发,但是之后立即访问而没有任何错误消息!

1 个答案:

答案 0 :(得分:5)

在关闭表单时Access无法正常清理表单对象。

这意味着:如果对象具有对表单的开放引用,则该表单对象将保留。如果没有引用,则只能由垃圾收集器将其删除。

该表单的第一个版本造成​​了内存泄漏:表单对象Form_Form1引用了Class1(通过MyClass1变量),Class1包含了一个通过theForm变量对表单对象的引用。这导致了参考循环。终止处理程序不会触发,因为该类从未终止,它会无限期地保留在内存中,并且关闭并重新打开该表单只是打开了该类的新实例。

第二个版本引起了一个问题:当引用循环中断时,对Form1的引用首先被释放(因为Class1上仍然有对Form1的引用),导致垃圾收集器进行清理,然后释放对Class1的引用,垃圾收集器尝试清理Class1,包括文本框对象SomeTextbox,导致自表单以来就发生了硬崩溃对象已被清理,文本框对象无效。

解决方案是通过首先删除对Class1的所有引用来打破引用循环。这不会导致崩溃。

Private Sub Form_Unload(Cancel As Integer)
    Set myClass1 = Nothing
End Sub

这将导致垃圾收集器首先清除Class1实例,释放对Text0的引用,然后清除表单对象,因为没有人对此有开放的引用。