Share via


Visual Basic Concepts

Circular References and Object Lifetime

In normal program operation, an object is not destroyed until all references to it have been released. This has implications for object lifetime. The procedures in this topic illustrate this by allowing Thing objects to indulge in a little Narcissism.

Note   This topic is part of a series that walks you through creating a sample ActiveX DLL. It begins with the topic Creating an ActiveX DLL.

To add the StuckOnMyself property to the Thing class

  1. In the Project Explorer window, double-click Thing to bring the code window for the class module to the front.

  2. Add the following code to the Declarations section:

    ' Private data for the StuckOnMyself property.
    Private mthStuckOnMyself As Thing
    Private mblnStuckOnMyself As Boolean
    
  3. On the Tools menu, click Add Procedure to open the Add Procedure dialog box. In the Name box, type StuckOnMyself. Click Property and Public, then click OK.

    Modify the property procedures as follows:

    Public Property Get StuckOnMyself() As Boolean
       StuckOnMyself = mblnStuckOnMyself
    End Property
    
    Public Property Let StuckOnMyself(ByVal NewValue _
          As Boolean)
       mblnStuckOnMyself = NewValue
       If mblnStuckOnMyself Then
          Set mthStuckOnMyself = Me
       Else
          Set mthStuckOnMyself = Nothing
       End If
    End Property
    

    This code illustrates the power of property procedures. When a user requests the value of the StuckOnMyself property, the Property Get procedure is called, and simply returns the value of the module-level Boolean variable mblnStuckOnMyself.

    When the user sets the value of the StuckOnMyself property, the Property Let procedure is called. After assigning the new value to the module-level Boolean variable mblnStuckOnMyself, the Property Let either places a reference to the Thing object (Me) in the module-level variable mthStuckOnMyself, or sets the variable to Nothing.

For More Information   Property procedures are the best way to implement properties for objects provided by components. See "Implementing Properties in Components" in "General Principles of Component Design."

What Are Circular References?

A circular reference occurs when two objects hold references to each other — or when an object holds a reference to itself, as in the StuckOnMyself property. You can read more about circular references in "Dealing with Circular References" in "General Principles of Component Design."

The next procedure adds code to TestThing to demonstrate circular references by selectively exercising the StuckOnMyself property. It also illustrates another way to create an object.

To exercise the StuckOnMyself property from TestThing

  1. In the Project Explorer window, double-click Form1 to bring the form to the front.

  2. Double-click the Temporary Thing button to open the code window, and add the following code to the Command5_Click event procedure:

    ' Button "Temporary Thing".
    Private Sub Command5_Click()
       Dim thTemp As New Thing
       thTemp.Name = InputBox( _
          "Enter a name for the temporary Thing", _
          "Temporary Thing")
       ' Create a circular reference if the check box
       '   captioned "Stuck on itself" is checked.
       If Check1.Value = vbChecked Then
          thTemp.StuckOnMyself = True
       End If
    End Sub
    

No code is needed for the check box. If Check1 is checked, the Command5_Click event procedure sets the StuckOnMyself property of the new Thing object.

Notice that instead of explicitly creating a new Thing with the New operator, the Temporary Thing button uses a variable declared As New, allowing implicit creation of the object. The next procedure demonstrates this. It also shows how object lifetime is affected by variable scope, and illustrates the effects of circular references on object lifetime.

To demonstrate circular references in TestThing

  1. Press CTRL+F5 to run the project group.

  2. Click Create New Thing. In the InputBox, type the name Long Term Thing, and then click OK. In the Immediate window, notice the messages from Sub Main and the Thing’s Initialize event.

  3. Click Temporary Thing to create a very short-lived Thing. Because the object variable thTemp that holds the reference to this Thing is a procedure-level variable, its lifetime — and hence the lifetime of the object — is limited to the execution of the procedure.

    You will first see an InputBox, because Visual Basic must evaluate the code to the right of the equal sign before assigning the result to the new Thing object’s Name property.

    Before you enter a name in the input box, look in the Immediate window. There hasn’t been an Initialize message from the new Thing object, because it has not yet been created. Because the variable thTemp was declared As New, a Thing object will be created the moment one of its properties or methods is invoked — and not a moment sooner.

  4. Type any name you like in the InputBox and then click OK.

    You’ll see two messages in the Immediate window, an Initialize message and a Terminate message. The Initialize event occurs the moment the name from the InputBox is assigned to thTemp.Name. Visual Basic finds that thTemp contains Nothing, creates a Thing object, and places a reference to the object in thTemp.

    Although the value of the DebugID property has already been set — it’s what happens first in the Initialize event — the Name property is still blank. This underscores the fact that the Initialize event occurs before any other code is executed, or any properties are set.

    Only after all this has happened can Visual Basic assign the value from the InputBox function to the Thing’s Name property. That’s a lot of activity for one line of code.

    But wait, there’s more. As soon as the Thing is created, the Command5_Click event procedure ends. The variable thTemp goes out of scope, which is exactly the same as if it had been set to Nothing. There are no more references to the temporary Thing, so it is destroyed. Its Terminate event displays its properties, including the name you assigned to it.

  5. Press Ctrl+Break to enter Break mode, then press F8 to enter Single Step mode. Click Temporary Thing to enter the Command5_Click event procedure.

    Press F8 once more, to move to the line of code that sets the Thing’s name:

    Before you go on to the next step, you might take a guess at what the next line of code will be.

  6. Press F8 to execute the InputBox statement. Type any name you like in the InputBox, and then click OK.

    Because you’re debugging the ThingDemo component in the same environment as the test program, Visual Basic can step directly into the component’s code from the test program.

    Continue pressing F8 through the code for the Initialize event, the DebugID property, and the Terminate event. When you reach the last line of Class_Terminate(), press F5 to return to Run mode.

    In-process debugging is a powerful tool for learning about the order in which events occur in a component.

  7. Check Stuck on itself, and then click Temporary Thing again, to create a Thing with a reference to itself. Type the name Renegade in the InputBox, and then click OK.

    In the Immediate window you will see an Initialize message for the Thing, but no Terminate message. The object was not destroyed when the variable thTemp went out of scope, because there was still a reference to the object — in the mthStuckOnMyself variable belonging to the StuckOnMyself property.

    When will this erstwhile temporary Thing be destroyed? If you could set the Renegade Thing’s StuckOnMyself property to False, there would be no more references — but TestThing can’t do this, because the program no longer has a reference to use to call StuckOnMyself!

    The object is orphaned, and will go on taking up memory until the DLL is unloaded. As you’ll see in a later procedure, the circular reference in the Renegade Thing’s StuckOnMyself property will keep the entire DLL in memory.

  8. Click Release the Thing to destroy the Thing you named Long Term Thing, observing its Terminate message in the Immediate window, and then click Temporary Thing several times. Enter a new name for each "temporary" object.

    As with the first Renegade object, you won’t see any Terminate messages for these objects in the Immediate window, because the circular references will keep them from being destroyed.

  9. On the Thing Demo dialog box, click the Close box to return to design mode. Presto! Like magic, a whole series of Terminate messages appear in the Immediate window.

    When Visual Basic shuts down TestThing, it also shuts down ThingDemo. Being the tidy sort, it clears all of ThingDemo’s object variables — including the self-references being held by the Renegade Thing and its cohorts.

As you can see, it’s important to avoid keeping extra object references in an in-process component. A client may create and release hundreds of objects while using a component. If they all persist in memory the way the renegade Things did, performance will eventually degrade.

For More Information   Circular references are discussed in "Dealing with Circular References," in "General Principles of Component Design," and in Appendix A, "ActiveX Component Standards and Guidelines."

Step by Step

This topic is part of a series that walks you through creating a sample ActiveX DLL.

To See
Go to the next step Adding a Form to the ThingDemo Project
Start from the beginning Creating an ActiveX DLL.