Reputation: 187
For a VBA program that uses a single object variable multiple times, is it best to reset the variable in the loop without first setting the variable to nothing (which can be done at the end outside of the loop), or should the variable always be set to nothing after each instance of use. From a reference counting perspective it seems best to set to nothing as I never have need for the previous state of the object once it has been changed to the next variable (I'm not sure how one would retrieve the previous state but I gather that there must be a function of such from this question). Another relevant question that I examined can be found here.
The application which triggered this question is one where I cycle through many Word documents, pulling relevant information from each of them. See example below:
dim objDoc as object, objWord as object
Set objWord = CreateObject("Word.Application")
for r=2 to 40
'Some code to set filename
Set objDoc = objWord.Documents.Open(filename, ReadOnly:=False, Visible:=False)
'Code to retrieve relevant info from document
objDoc.close
Set objDoc = nothing 'Should this go here?
next r
set objDoc = nothing 'Or should it go here?
objWord.Quit
The Question: Does it matter when you set an object variable to nothing in terms of code efficiency or problem avoidance. I have highlighted above two options for where I believe that should go with the third option being that it doesn't matter when I set the variable to nothing
Upvotes: 3
Views: 5481
Reputation: 13386
Inside the loop
Which should be easily inferred from the following equivalent code:
dim objWord as object
Set objWord = CreateObject("Word.Application")
for r=2 to 40
'Some code to set filename
With objWord.Documents.Open(filename, ReadOnly:=False, Visible:=False) 'Set and reference your document object
'Code to retrieve relevant info from document
.Close
End With 'this “disposes” the referenced document object, so you also set it to Nothing
Next r
objWord.Quit
Of course, there’s is room for further application of the same concept to objWord, too:
With CreateObject("Word.Application") ‘ instantiate and reference your Word object
for r=2 to 40
'Some code to set filename
With .Documents.Open(filename, ReadOnly:=False, Visible:=False) 'Set and reference your document object
'Code to retrieve relevant info from document
.Close
End With 'this “disposes” the referenced document object, so you also set it to Nothing
Next r
.Quit
End With 'this “disposes” the referenced Word object, so you also set it to Nothing
Upvotes: 0
Reputation: 332
Short answer: In the loop.
Long answer: From a problem avoidance perspective, you should set it to 'nothing' when you are immediately done with it. The problem you are going to be trying to catch is an unintended reuse. You want your code to explode if you ever do accidentally reuse the variable instead of having to debug it later when it gives weird results. Generally speaking you shouldn't reuse variables because it's all too easy to make a mistake when refactoring and the variable ends up containing the wrong value.
VBA unfortunately doesn't care if you use a variable outside of the innerscope it was dim'ed in. The following is unfortunately legal and more modern languages don't let you do this:
Sub CountInsideLoop()
Dim i As Long
For i = 1 To 3
Dim count As Long
count = count + 1
Next i
' count value will be 3
Debug.Print count
End Sub
Upvotes: 3
Reputation: 71187
I would argue that, generally speaking, having a variable that has two or more different meanings depending on context, is making the maintainer's life harder than it needs to be. In this case, whether you put it inside or outside the loop scope should make no difference at all - in fact whether Set objDoc = Nothing
is specified at all, should make no difference either.
You're not showing the rest of the procedure, but as far as I can see there's not really a reason why that particular procedure wouldn't be doing 10,000 other things.
The code you've shown should be written in a bunch of procedures and functions.
'Some code to set filename
You need a function that implements the logic to get a filename, and returns a String
.
'Code to retrieve relevant info from document
You need a procedure that retrieves relevant info from a document.
Pull the body of the loop into its own procedure:
For r = 2 To 40
filename = LogicToGetTheFilename(r)
ProcessWordDocument wordApp, filename
Next
Much higher abstraction level here - with proper naming you know what that loop does at a glance.
Private Sub ProcessWordDocument(ByVal wordApp As Object, ByVal filename As String)
Dim docx As Object 'Word.Document
Set docx = wordApp.Documents.Open(filename, ...)
'do stuff
docx.Close
'Set docx = Nothing
End Sub
Now that the Word document object variable is living in its own scope, VBA should appropriately destroy the underlying object whenever execution exits that scope (i.e. once per loop iteration).
Because refactoring the loop into proper abstraction levels means the Word document only lives for a single iteration, I would argue (rather strongly) that Set objDoc = Nothing
belongs inside the loop body.
Upvotes: 3