何时应该销毁或将Excel VBA变量设置为Nothing?

38

过去两年里,我一直在自学Excel VBA。我认为,在代码段结束时,有时会适当地释放变量。例如,我从Ron de Bruin的将Excel转换为HTML代码中看到了这样的做法:

Function SaveContentToHTML (Rng as Range)

        Dim FileForHTMLStorage As Object
        Dim TextStreamOfHTML As Object
        Dim TemporaryFileLocation As String
        Dim TemporaryWorkbook As Workbook

...

        TemporaryWorkbook.Close savechanges:=False
        Kill TemporaryFileLocation
        Set TextStreamOfHTML = Nothing
        Set FileForHTMLStorage = Nothing
        Set TemporaryWorkbook = Nothing

End Function

我在这方面做了一些搜索,除了如何执行此操作的内容外,没有太多内容,在一个论坛帖子中看到一句话说不需要清除任何本地变量,因为它们在End Sub时就不存在了。根据上面的代码,我猜想在End Function或其他情况下可能并非如此,但我还没有遇到。

所以我的问题归结为:

  • 是否有网站可以解释变量清理的时间和原因,而我只是没有找到?

如果没有,有人能否在这里解释...

  • 何时需要清理Excel VBA的变量,何时不需要?
  • 具体而言...是否有特定的变量使用(公共变量?函数定义的变量?)会在内存中保留更长时间,因此如果我不清理自己,可能会引起麻烦?

3个回答

72

VB6/VBA使用确定性的方法来销毁对象。每个对象都存储了对自身的引用计数。当引用计数达到零时,对象被销毁。

当对象变量超出作用域范围时,它们保证会被清理(设置为Nothing),这会减少它们各自对象中的引用计数。不需要手动操作。

只有两种情况需要显式清理:

  1. 当你希望在其变量超出作用域之前就销毁对象时(例如,你的过程执行时间很长,并且对象持有资源,所以你想尽快销毁对象以释放资源)。

  2. 当两个或多个对象之间存在循环引用时。

    如果objectA存储到objectB的引用,而objectB又存储到objectA的引用,则这两个对象将永远不会被销毁,除非你通过显式设置objectA.ReferenceToB = NothingobjectB.ReferenceToA = Nothing来断开链条。

你展示的代码片段是错误的。不需要手动清理。甚至进行手动清理是有害的,因为它会给你一个更正确代码的错误印象

如果你在类级别有一个变量,它将在类实例被销毁时被清理/销毁。如果你想要提前销毁它(参见项目1.)则可以这样做。

如果你在模块级别有一个变量,它将在程序退出时被清理/销毁(或者在VBA中,当VBA项目重置时)。如果你想要提前销毁它(参见项目1.)则可以这样做。

变量的访问级别(公共 vs. 私有)不影响其生存时间。


1
谢谢GSerg,这个解释清晰简洁,让我更好地了解了变量管理。还有一个问题:你说“变量的访问级别不影响其生命周期”。你知道这个生命周期是否受到活动代码首次设置值(开始)和代码到达“End”标记(完成)之间的限制?或者它是在项目重置时结束?我尝试通过设置在另一个模块中定义的公共变量的值来测试这个理论,但该变量从未出现在本地窗口中,因此我无法观察它何时被删除。 - Instant Breakfast
@KarlRookey 公共 vs 私有与类级别 vs 过程级别是不同的。公共 vs 私有不影响生命周期。类级别意味着变量的生命与类一样长。模块级别意味着变量的生命与项目未重置的时间一样长。请注意,“生命”和“包含某些内容”并不是同义词。变量可以存在且包含“Nothing”。 - GSerg

7

VBA使用垃圾回收器来实现引用计数

同一个对象可以有多个引用(例如,Dim aw = ActiveWorkbook会创建对活动工作簿的新引用),因此当清楚没有其他引用时,垃圾回收器才会清理对象。将其设置为Nothing是显式减少引用计数的方法。当退出范围时,计数会隐式减少。

严格来说,在现代Excel版本(2010+)中,将其设置为Nothing并不是必要的,但在旧版本的Excel中存在问题(解决方法是显式设置)。


1

我至少有一种情况,其中数据不会自动清理,最终会导致“内存不足”错误。 在一个用户窗体中,我有:

Public mainPicture As StdPicture
...
mainPicture = LoadPicture(PAGE_FILE)

当UserForm被销毁后(在 Unload Me之后),加载在mainPicture中的数据所分配的内存没有被释放。我不得不添加一个明确的方法。
mainPicture = Nothing

在终止事件中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接