Destroying Forms
by Bruce Beacham

PROBABLY the first thing to remember is that opening or closing a form does nothing to either create or destroy the objects within the form, nor does it even create or destroy the form itself. Opening a form makes the form visible (like switching its lights on) and the visual objects workable; the reverse is true when you close the form. But the form and its components all still exist after the form has been closed! It has not been destroyed. You can re-open a closed form time and time again.

So can we ever get rid of a form object from memory?

The form, and the objects defined in its constructor code, are normally created when you do this:
 
 
f = new form()
   

(Of course, that is a stock form, which has no components yet defined.   If you are using a subclassed form, which you create with f = new mySubclassedForm(), you may have previously defined some component objects within it. But let’s keep it simple!)

Consider the form object as a Christmas tree decoration. It is created by being instantiated — which is done with the phrase new form().

You can enter that phrase as a command in the Command window — try it.  But, even though a new form is instantiated by dBASE and momentarily exists, new form() by itself has not provided anything that the decoration can be attached to; hence the new form object will immediately plunge to the ground and be destroyed. So are all its components. Object gone, memory released.

If you do this:
 
 
? new form()
   

the same thing happens — there is no remaining hold on the form.  But this:
 
 
f = new form()
   

creates an object and the reference to it is held within the memory variable, f. As long as this variable exists, the decoration is held safely in the air. If the variable ceases to exist, then the decoration is unsupported and is destroyed.

If you then issue:
 
 
myvar = f
   

you now have a second reference to (i.e., support for) the object;  thus even if the variable f is released:
 
 
release f   // the release of a variable
   

the decoration/object is still supported by the myvar variable's reference to it.

Issue this:
 
 
myvar = "Ha ha, you're destroyed"
   

and myvar no longer refers to the object — it now holds a string — so the object falls to the ground…

Now let’s look at the scope of a procedure that calls a form. f is usually a local variable so when the procedure terminates, f as a variable is destroyed. Thus there is no reference to (support for) the object and it too is destroyed. But if you create an external reference to the form, or even a second reference to the form as a new property of a component of the form (such as a pushbutton, as thus: f.pb1.newref = f), then the form is not destroyed when the local variable is released.

Just to complicate things a bit, you can make an external reference to a component — but when the local variable holding the form is released, both the form object and the component object will still be released automatically. And you can make an external reference to a query which has a form as its “parent” (which is a special property of many objects), and the natural destruction of the form will still occur — although in this case the query, being a free-standing object not dependent on the form for its existence, will remain in existence because of the external reference.

That's how it happens naturally in a program. However, you can destroy the form object yourself — many objects hold the seed of their own destruction in the release() method. Or you can use the command:
 
 
release object <oref>
   

But you don't really need to unless you are concerned about memory leakage, a much-discussed topic.

Now some words of warning. In certain circumstances a form can defy the laws of gravity. You saw above that when a form is no longer held aloft by an external reference, then it falls to the floor and is destroyed.

But it can also be held aloft by an internal reference to itself.

Such a reference would be a reference to the form which the programmer creates if he intentionally store a reference to the form in a custom property of a component of the form. Consider this:
 
 
f = new form()
f.pushbutton1 = new pushbutton(f)
   

(You may recall seeing lines like that in the code produced by the Form designer).

So far, so good.   Destroy the variable f :
 
 
release f   // the release of a variable
   

and the form and the pushbutton in it both disappear.

To prove it, try to find the form:
 
 
? findinstance("FORM")
   

and you should see that nothing (literally!) appears in the Results pane. (This trick only works in a fresh clean instance of dBASE as you may already have created other forms which are still littering the “object heap”).

But do this:
 
 
f = new form()
f.pushbutton1 = new pushbutton(f)
f.pushbutton1.myformref = f
   

and destroy the variable f:
 
 
release f
   

and try to find the form:
 
 
? findinstance("FORM")
   

and you will see the word Object in the results pane.

But, I hear you cry, the pushbutton always contains a reference to the form:  you can see that from the on-line help. And by inspecting the object:
 
 
f = new form()
f.pushbutton1 = new pushbutton(f)
inspect(f.pushbutton1)
   

you can see a native property called “form” in the list of properties of the pushbutton — and that's a reference to the form, isn't it?

The same is true of what the form designer does when you drop a query in the form:
 
 
f = new form()
f.query1 = new query()
f.query1.parent = f
   

Yes indeed. The difference is that dBASE knows about these and that they are always there, so it ignores those references when considering whether there are any remaining references to a form. Consider carefully what would have to happen for dBASE to detect all the references there may be to the form, and all the references to those references, and whether they are all truly only references held within the form. You will quickly see that it would in practice be impossible for dBASE to be sure that all references are internal.

So if there is a possibility that you have created your own references to the form or any of its components, then you must, sadly, take hold of the form hanging like a Christmas tree decoration, and throw it to the ground yourself:
 
 
release object f  // the release of the object itself, not the variable holding it.
   

Is it gone?
 
 
? findinstance("FORM")
   

returns an empty value, so that’s good.  What about the variable?
 
 
? f
   

Whoops!  That returns Object. How can that be?

The answer is that the current status of the variable is that it is an object pointer. It is not a character variable at the moment, nor does it hold a number or a logical. The variable is currently an object pointer. Try this:
 
 
? type("f")  // remembering that f must be a private variable for this to work, not a local.
   

and the response is o.But what does it point to?  The answer is – nothing!  Try this:
 
 
? empty(f)
   

and the response is true. To prove it, try this:
 
 
? f.classname
   

You get an error message that you have attempted to access a released object. There is no object at the end of the pointer, which is why findinstance() returned a null report, i.e., that there are no objects in existence of the class specified.

It is unwise to leave disconnected object pointers lying around, so if your variable f is not going to be released by the ending of the routine it appears in, then you should release the variable yourself:
 
 
release f 
   

After which you can be confident that the form object is truly gone, the memory has been released and you have no loose ends.

A complication:  How do you break the link to an object when the object pointer is the property of an object? Let us suppose you have a form and it has a property pointing to an array, and you wish to release the external array object:
 
 
f = new form()
a = new array(1, 3)
f.a = a

release object a
? findinstance("array")  // returns a null - the object itself has gone.
? a

   

That returns Object - but that is easily dealt with:
 

release a
   

But
 
 
? f.a
   

also returns Object, and you cannot release an object property. But you ought to do something, as a loose end is potentially troublesome.

The solution is to change the type of the object property, usually to a null (although, as described above, changing it to a string would work just as well):
 
 
f.a = null
   

That is a safe state to leave the property in. It is also quick way of releasing any object that started out as that form property ( f.a = new  array()) and was never the target of any other object pointer.

One further trick. Let us say that you have done this:
 
 
f = new form()
f.pushbutton1 = new pushbutton(f)
f.pushbutton1.myformref = f
   

You decide to destroy the form by destroying the variable:
 
 
release f
   

You test for the form's existence and find it still exists, because it has a circular reference:
 
 
? findinstance("form")
 

But how are you now going to release the object without going to the extreme step of stopping and restarting dBASE? You have lost your reference to it, because you have released the variable.

You do it by attaching a handle (which becomes an object pointer) to the result of findinstance(), thus:
 
 
new_f = findinstance("form")
? new_f             // returns "Object"
release object new_f
release new_f
   

All gone.

So you see that it takes more than just closing a form (switching its lights off) to kill the form object. But you may want always to kill your form upon closing it.

It is a general principle of encapsulation that you put as much relevant code into the object as possible, rather than remember all the details each time you want to use an object. Many dBL programmers therefore use the form.release() method to get rid of their forms when they want to close them. This command can be included in the form's canClose() or onClose() methods, so that it happend automatically when you release the form through any of these methods. (Remember to get all the information you want out of the form before you release it!). It is like release object f in that it can release the form despite the circular references.

It works well even if you allow a form to be released naturally, that is by the expiry of a local variable holding the form object reference on exit from the procedure that instantiated the form. For example:
 
 
subprg()
"F = ", findinstance("form")

Function subprg
  local f
  f = new form()
  f.pb = new pushbutton(f)
  f.pb.myform = f
  f.release()
  return
   

You should find that the form is released despite the circular reference that, in the absence of a form.release(), would normally cause the form to survive in existence after exiting the function.

As usual, there is something to keep in mind. Findinstance() is an excellent tool for getting at anything within the scope of the instance of dBASE itself that you have running. It can therefore reach objects you didn't know still existed, and it is certainly capable of picking up the wrong object of the same class as the one you want. You may need to scroll through the object heap looking for the one you want (using findinstance("myclass", fi)  where fi is the result of the previous findinstance() — see the on-line help). You will have to decide how to identify which instance of MYCLASS is the one you want to grab. You can look at its properties and methods once you have a handle on the object, and that will probably have to be the way you get your clues. Good hunting!


The author would like to thank Robert Newman and David L. Stone, his proofreaders, for the improvements they brought to this text.