Tech News Back Issues Issue: 062106
Object Reference Variables
It occurred to me that we should examine Object References before going into the more complex uses of Object Instances (Data Objects, Extension Objects and Session and Statement Objects). I have also received a few requests (well, OK... one...) for an explanation of how Object References work and for some suggestions of when they are the best choice. While I consider this to be partially a matter of personal preference (like so many things in Omnis Studio), I will muse about the "best choice" issue near the end of this article. Perhaps the examples I give along the way will help make the case for their use.
We have so far examined Function Objects and Helper Objects, which I would generally deploy in a "global" fashion (at least for most of the examples I gave in those articles) at the Task level of scope. The way I use them, I have no need to pass them as parameters (unless I am developing a multiple Task application). But before we address the subjects of Data Objects and Session and Statement Objects, which may very well need to be passed from place to place occasionally, an understanding of Object reference variables is essential.
As we do more work with Object Instances, especially when we have begun to use instance variables for manipulating and storing data within our Objects, we begin to bump into situations where we might like to access an Object (variable) from outside its normal scope. For example, we might want to pass an Object containing important data from a window instance to a report instance - or even to another window instance spawned from a different window class. But if we pass an Object variable as a parameter, we get a new and independent copy of that Object in the receiving method - and that copy only has a local scope, being a Parameter variable. If we then copy that into an instance variable for the instance that contains the method that received the parameter, we have yet another duplicate of our original Object. It's sort of like the reverse of the drinking song "99 Bottles of Beer on the Wall". If we "take one down and pass it around", we end up with more Object Instances than we began with rather than fewer!
And there are other places we may want to push an Object Instance into. Complete Object Instances do not always play well as cell values in a list variable, but there are times when it would be nice to be able to work with them in that way. Even if we manage to put an Object Instance into a cell in a list variable, it is again only a copy of the Object that we originally manipulated outside of that list. Perhaps we would like to have pointers to a single Object Instance on multiple lines of that list rather than multiple copies of the original, so that when the original Object Instance is updated, all lines pointing to that Object are updated as well. Item references to Object variables are only partially successful at this.
Any changes that we make to values contained within any of these copies will not be passed back to our original Object. The copies attain a life of their own as soon as they are created, going on their merry way completely independent of one another. If we are working with Object variables within multiple instances of the same window Class (for example), we can certainly share a common Object by using a class variable. But we do not have a variable type that allows us to retain the instance level of scope, but span across instances of different classes.
A number of interesting solutions have been attempted by those who have encountered these difficulties. Old-time Omnis programmers (who have not been hit in the head by the wand of the OO fairy often or hard enough) are tempted to simply broaden the scope of the variable and forego the benefits of using instance variables entirely in favor of task or memory-only File Class variables. No need to pass a variable from place to place, but no surgical precision in the use of application resources either. Not a good choice.
A second technique was to pass references to Object variables to the $construct methods of other instances and on to Item reference variables within those instances. While this would work for many cases, there was no way to know how many such references had been passed on from the point of view of the original Object Instance - or where they might be. There was also no way to know whether such a reference was still viable before using it, because the original Object Instance may have been destroyed since the reference was established. Housekeeping to avoid errors from occurring was a difficult chore.
And there were even more complex schemes hatched to live with having multiple instances of the same Object, but to somehow keep them in synch when a change was made to the value of any internal variable in one of them. Once again, the complexities involved made this a real nightmare. There has to be a better way!
The New Built-In Solution
Omnis Studio version 4 introduced us to a more viable and complete solution to these problems in the form of a new data type for scoped variables. I emphasize scoped variables because this is the first time in Omnis history when a type of variable cannot be assigned to a data-bound variable (File or Schema Class variable) or stored in a native Omnis datafile. I am not including the Field reference type (which can only be applied to a Parameter variable) in that statement - and yet there are definite similarities between the Object reference and Field reference data types. Both embody a mechanism for creating an alias for a variable that passes through any actions (value changes, etc.) directly to the original. In the case of Object reference variables, the methods of the original Object Instance can also be applied to this variable in the same way that we use Item reference variables - and the $assign() method for Field reference variables.
The purpose of an Object reference variable is to allow us the freedom to pass around an Object Instance without making a new copy of it. This makes more efficient use of RAM resources by the application as well as giving us the consistency of applying actions from one Object Instance access point to the next. As we will see, we have also been given some useful housekeeping tools to keep track of our references as well.
But we can't just make references to Object variables that we have already created. Object variables are containers of Object Instances. Object reference variables are only pointers to disembodied (uncontained, if you will) Object Instances - and we can only use them with Object Instances spawned in a special way. Since no term has yet been created for such Object Instances, let us call them "referentiable" Object Instances.
The Birth of an Object Reference
Remember that we learned in an earlier article that we can dynamically spawn an Object Instance by executing the $new() method of an Object Class. The return value of this method is an Object Instance. We can make it an instantaneous Object by following the $new() segment of the notation string with the name of a public method of that Object, which will perform its function just before the Object Instance vaporizes. Or we can capture that Object Instance in an Object variable of appropriate scope. This allows us to continue to use that same Object Instance until the Object variable that contains it is destroyed - subject to the limitations of scope of that variable, of course. Either the Do command or the Calculate command can be used to assign an Object value spawned by $new() to an Object variable. So the following two lines of code are equivalent:
The "5" in these examples is a value sent as a parameter to the $construct method of the Object Instance. Also notice that, at least in single library applications, we do not need to preface the $objects notational segment with $clib or other references to the library containing the Object Class.
And, of course, we can set up an Object variable to immediately spawn a contained Object Instance by giving it a subtype value of an Object Class or Object Extension when we define that variable in the Variable Definition Pane of the Method Editor. This is what I refer to as a "static" Object variable - which is mostly outside the discussion here.
Object reference variables are populated in essentially the same way as dynamic Object variables. Omnis Studio version 4.0 introduced a new method of an Object Class made specifically for this purpose. It is the $newref() method:
On execution, this method creates both a new Object Instance (the nature of which we will discuss at length in this article) and an Object Reference value that points to it. The Object Instance is named directly from the Object Class from which it sprang, but an underscore character and an integer are appended to that name to make the complete instance name unique. The Object Instance is then sent off to a special place to live (more on this later) and the Object Reference that points to it is passed to a variable of Object reference data type.
For all the ways that we have used Object variables, we use Object reference variables exactly the same. In fact, if we had previously used dynamic instantiation for populating an Object variable and we now decide to make it an Object reference variable, the only thing we must do besides changing its data type is to change the $new() method that spawns the Instance into $newref(). The two types of variables are operationally that similar! But there is one big difference...
If we use an instantaneous Object reference (following the $newref() method with a method name from the Object Instance in the same notation string), the referentiable Object Instance that is also spawned continues to exist after the reference value disintegrates. In fact, we lose contact with that Object Instance (except through the $insts group of the Object Class from which it was derived) if we do not capture the Object Reference value into an Object reference variable, so this is probably not a good idea.
Passing Object References
If we wish to pass an Object reference value as a parameter, we simply supply the name of the Object reference variable that contains that value as a parameter of the method call, just like we would any other value. The Parameter variable in the receiving method must also be of the Object reference data type. On execution, that Parameter variable becomes another access point to the same Object Instance as the original Object reference variable.
If we simply want to duplicate the reference into another variable that is also in scope, we again use the Calculate command or the $assign() method of the target variable. We can also use the Do command since Calculate and Do are equivalent. So if we have already populated the Object reference variable dataObjRef1 and now want dataObjRef2 (also defined as Object reference data type) to point to the same Object Instance for some reason, we could perform any of the following commands:
We do not use the Set reference command for this. It only works for assigning values to Item reference variables. Object references are not Item references. We pass their values to other variables of the same data type exactly as we would variables of any other data type.
Since cells in a list variable are also variables in their own right, we use the same techniques to assign an Object reference value to a cell in an Object reference column in a list. For example, if listVar was defined to include a column of Object reference type named dataObjRef2 and we want to copy the reference from dataObjRef1 into the cell on line 3, we would use:
Both dataObjRef1 and the list cell now point to the same Object Instance. Note that the cell in the list does not point to the dataObjRef1 variable, so we can use $newref() and repopulate that variable with a reference to a new Object Instance and the list cell will still point to the original Object Instance. At some later time we could return and copy the reference from the list cell into dataObjRef1 if we wish.
With Greater Power Comes Greater Responsibility
If we use this new tool, we need to take responsibility for cleaning up our own messes. When we use an Object variable, the Object Instance contained in that variable is destroyed automatically when the variable is destroyed. This happens when the item (method, instance or task) that contains that variable is closed. Omnis Studio manages this housekeeping task for us.
But when we use an Object reference variable and then destroy it, the referentiable Object Instance to which it pointed continues to exist. Only the reference is destroyed. The Object Instance continues to live on in RAM because there may be other Object reference variables that still need it. It is up to us to build the code that determines whether it is safe to destroy the instance and then to take care of that chore. The only times when a referentiable Object Instance is automatically destroyed are when the task that contains it is destroyed or until we quit Omnis Studio itself.
So how do we destroy these ethereal Object Instances on demand? We have been given a method for an Object reference variable named $deleteref(). This method is used to delete the "referentiable" Object Instance to which the Object reference variable points. When it is executed, the $destruct method of that Object Instance is also executed as part of the process. As with any other instance, we cannot keep the destruction from happening from the $destruct method - it is too late for that. But we can perform other housekeeping duties as the instance is on its way out.
We cannot apply the $deleteref() method directly to the Object Instance itself, but there is a way of locating an "orphan" referentiable Object instance in case we destroy all references to it. We can do this using the $listrefs() method. (This method lists referentiable instances, not references to them as the name might imply.) This method can be applied to a couple of places in an application. We can apply it to an object source (Object Class or Object Extension) or we can apply it globally (directly to $root - which means we can apply it without a qualifier). Rather than reporting all instances of an object source, this method returns a list of all referentiable instances of that source. When applied to $root, it reports all referentiable Object Instances that currently exist in the application.
The one-column list that is returned by the $listrefs() method is a list of Object reference values, so we can apply the $deleteref() method to one of those cell if we need to - or all of them using the $sendall() method. The Omnis Programming manual provides some useful examples of this.
But What If We Really Do Want A Duplicate Instance?
There may be occasions when we will want to make a copy of a referentiable Object Instance, just like the default behavior for passing around "normal" Object Instances. The creators of Omnis Studio have thought of this need as well. They have given us the $copyref() method for this purpose. We apply this method to an existing (and populated) Object reference variable (or list cell). The return value of this method is another Object reference, just as with the $newref() method. And just like $newref(), $copyref() spawns a new referentiable Object Instance - this one an exact duplicate (except for its name, of course) of the instance to which the original Object reference variable points.
So if dataObjRef1 is already populated and we want to create a duplicate of its Object Instance and have dataObjRef2 point to it, we would execute:
We can also use the Do command, with or without the $assign() method, to perform the same chore. These two instances are then completely independent of one another, if that is what we want. Performing the $listrefs() method on the original Object Class will then show (at least) two referentiable instances in the list it generates for that class.
Testing For Validity
But what if we mistakenly destroy an Object Instance while at least one Object reference variable exists that points to it? We still have no way of knowing whether there are viable Object reference variables pointing to a referentiable Object Instance, so this could very well happen. How can we know whether an Object reference that we wish to use is still valid without using it and getting an error message? The creators of this technology thought of that too. They have given an Object reference variable a method named $validref() that allows us to test whether the reference is still usable. This method returns a Boolean value with kTrue indicating that the reference is valid.
While it might well be considered overkill to use this method before performing any action involving an Object reference variable, there will be occasions where it is reasonable to perform such a test. Such is the case in an instance where an Object reference has been passed in from outside. We may well want to test for a valid reference if we have programmed the original window to execute the $deleteref() method of the original Object reference variable in that windows $destruct method. If that window was closed while this second window remains open, the reference passed to this second window will not be valid.
So Where Do These Object Instances Live?
An excellent question! The best that I can tell you is that they live somewhere within the Task Instance where they were spawned. They exist outside the normal levels of scope as we know them and are only accessible through Object reference variables. For lack of a better term, I have come to call this place the "Heaven for Object Instances". The Object reference variables that we use to access these Object Instances each have their own level of scope and the same Object Instance can be accessed by variables of different scope. So an Object reference held in a class variable on one window can be passed to a parameter variable in the $construct method (or some other method) of a different window instance and from there passed to an instance variable. But there is still only one Object Instance to which all these Object reference variables point (as long as the passing is done correctly).
We can perform a simple test to prove that a referentiable Object Instance is tied to a task, now that we understand all the tools. Here are the steps:
We could also have just built a list of all the referentiable Object Instances using $listrefs() both before and after destroying the task.
Useful New Feature of Object Reference Variables
Omnis Studio version 4.1 gave us a new and very useful option for Object reference variables. In Studio 4.0, we could not use the Interface Manager with Object reference variables to determine what properties or methods they might contain or to drag the names of those properties or methods to our code in the Method Editor. This was a really convenient feature of Object variables and it was a shame to lose it with Object reference variables.
So in Studio 4.1, we were given the option to specify a default Object Class for an Object reference variable as its subtype in the Variable Definition Pane of the Method Editor. When we do so, a dialog appears that states,
"The subtype of an Object reference is only used to allow the interface manager to function before the object has been created with $newref(). It has no other purpose."
So this is not a way to create a "static" Object reference variable. We must either use the $newref() method of an Object Class (which spawns a new Object Instance as well) or receive a value from another Object reference variable to populate an Object reference variable. It is our responsibility (once again) to make certain that the Object Class we choose for this developer aid is, in fact, the Object Class for which our variable will actually be used. If we choose the wrong one, a notational item we might drag from the Interface Manager may not exist at execution time. (Of course, the same thing can be said if we make changes to the Object Class after adding such items to our code as well.) As responsible programmers, we must make prudent choices.
So What Is The Best Choice?
As with so many things in Omnis Studio, it depends on a number of things - not the least of which is the preference of the programmer. Having said that, here are my preferences for using Object and Object reference variables:
I tend to use Object variables for "static" Objects - ones I don't intend to pass around. These are generally my Function and Helper Objects in single task applications, which are generally deployed as task variables (and so would be "globally" available anyway). But this group may also include instance or local variables that perform other jobs (Data Objects, for example) where I do not intend to require access to them from outside their scope. I also occasionally use instantaneous instances, which I lump into this category as well.
I use Object reference variables everywhere else, accepting the responsibility of managing memory usage and destroying unused Object Instances in a timely manner. I even use them for Function and Helper Objects at the task level in multiple task applications so that I can simply pass references to new tasks as they are launched and have truly global access to these application resources. Now that the issues of using Object references to Session Objects at the task level have been ironed out, I use them for that purpose as well. I just find them to be more flexible than Object variables, so I use them more.
Now that we have some grounding in the world of Object reference variables as well as Object variables, it is time to delve into the subject of Data Objects. That will be next month's topic.
As an aside, I recently published the second paper in my Advanced Technique Monographs series. The subject of this one is "Display and Formatting of String Segment Values". It covers a lot of territory in its 28 pages, from a historical perspective on how various data formatting features in Omnis Studio have evolved to a thorough discussion of input and display masks and Masked Entry fields to an in-depth look at the new format() function introduced in Omnis Studio version 4.1. I hope you continue to find these discussions useful. For more information on this Monograph and how to obtain it, visit my web site at:
Then click the "Purchase Information" link in the navigation bar on the left and click the name of the Monograph when that page appears for more detailed information.
Until next time...
Copyright of the text and images herein remains with the respective
author. No part of this newsletter may be reproduced, transmitted,
stored in a retrieval system or translated into any language in any
form by any means without the written permission of the author or
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7 is a trademark of Raining Data UK Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.