Search the Catalog
REALbasic: The Definitive Guide, 2nd Edition

REALbasic: The Definitive Guide, 2nd Edition

By Matt Neuburg
2nd Edition September 2001
0-596-00177-0, Order Number: 1770
752 pages, $39.95

Chapter 3
Objects, Classes, and Instances

In this chapter:
Messages and Dot Notation
Object Design Philosophy
Classes and Instances
Anatomy of a Class
An Instance Is Born
Referring to Instances
The Truth About Controls
Control Clones and Control Arrays
Being Careful with Instance References
Destruction of Instances

Consider programming as a way of thinking, and code as a way of speaking--a language. The programmer thinks about what the program is supposed to do, and then expresses this in code. An object is a programming construct which assists the programmer both in thinking and in speaking about the program, both in organizing and in coding. The program works as if it consisted of autonomous bundles of functionality--the objects. These objects are somewhat like the bundles of reality, the "things," the "objects," in terms of which we perceive and describe the natural world. Thus, the use of objects in programming makes the programmer's thought and speech about the program more natural, which in turn makes programming easier.

What are the objects of which a program is constructed? In general, that's up to the programmer, who, rather like some divinity creating a small universe, dictates both what objects should populate that universe ("Let there be light; let there be the sun; let there be the earth") and what those objects should do and how they should interact ("Let the sun make light to shine upon the earth"). Such power may seem daunting, almost paralyzing, rather than helpful. Where to begin? If there were an easy answer, there wouldn't be approximately six gazillion books about designing object-oriented programs.

REALbasic both enforces object-oriented programming and helps you with object-oriented design. Before you write a line of code, the construction of your program's universe has already been started for you. The program you are contemplating is to be an application; REALbasic is an application framework, and has stocked your program beforehand with objects that mirror the physical elements of an application's interface: windows, buttons, text fields, menu items, and so forth. Such physical elements of the interface are the objects of the "natural world" the user perceives when working with your application, and so they are the very objects in terms of which it is most natural to think and speak about the operation of your program ("This button should react to being pressed by opening that window").

REALbasic's built-in objects are useful and powerful in three ways:

REALbasic's built-in objects may be the only objects your program needs; if your program is fairly simple, that's quite probable. But if your program is more specialized or more complex, you will probably wish to extend REALbasic's objects by modifying them or adding new ones of your own. Either way, you will not be entirely freed from having to make intelligent object-oriented design decisions. At the very least, you will have to construct your program's interface, and you will have to decide how to allocate your code among the objects that implement that interface. To do this efficiently, in a way that makes the program do what you want it to do and as easy as possible to understand, maintain, and alter, you need to draw from two kinds of knowledge: you should understand REALbasic's internal object model and what the built-in objects do; and you should adhere to principles of good object-oriented design.

If this sounds daunting, don't worry; this book is here to help! Throughout this chapter and the rest of , I'll be describing the REALbasic object model, explaining how you work with objects in general and how they fit together to provide the architecture of your application as a whole. The rest of the book provides the details on each of REALbasic's built-in objects.

Messages and Dot Notation

As a programming construct, objects divide a program into pieces. The glue that unites those pieces to form a single working program is the ability of the objects to send one another commands and ask one another questions. The objects are autonomous, but they can communicate; this communication takes place by means of messages. In REALbasic code, the sending of a message to an object is expressed by dot notation.

Suppose, for example, that we have an object called MyWindow and we wish to tell it to turn blue. We could do this by sending it a TurnBlue message--assuming, of course, that the MyWindow object can accept a TurnBlue message. If it can, then we might say:

myWindow.turnBlue

This shows the basic dot notation syntax whereby messages are sent: the name of the object is followed by a dot, which is followed by the name of the message.

But how does this syntax fit into the larger syntax of the REALbasic language? The answer is that every message is one of two types, both of which are already familiar to us from Chapter 2: it operates, depending on the particular message, either as a subroutine call or as a variable name. As a matter of nomenclature, it happens that the subroutines that can be called by sending a message to an object are called methods, and the variable-like things whose values can be accessed by sending a message to an object are called properties. But syntactically and conceptually, they present nothing new. Since we already know how to use subroutine calls and variable names in code, we already know how to use messages as well.

To illustrate, suppose a particular message designates a method. That means the message operates as a subroutine call. As we know, subroutines can be either procedures or functions, so let's suppose this method, TurnBlue, is a procedure. Then, if that procedure takes no parameters, we might say:

myWindow.turnBlue

If the procedure takes one parameter (perhaps an integer telling how blue the window should turn), we might say:

myWindow.turnBlue 32

Now let's suppose we have a different method, HowBlue, which is a function taking no parameters (perhaps returning an integer telling how blue the window is). We might say:

dim yourBlueness as integer
yourBlueness = myWindow.howBlue()

All the other forms of syntax may also be used, as appropriate to a procedure or a function, taking the appropriate number of parameters.

Now, suppose a particular message designates a property. That means the message operates as a variable name. For example, the window might have a property MaxBlueness which is an integer, denoting the maximum blueness this window should allow itself to adopt. Then we might say:

myWindow.maxBlueness = 40

or:

dim theMax as integer
theMax = myWindow.maxBlueness

or:

myWindow.maxBlueness = myWindow.maxBlueness + 10

Thus, aside from dot notation itself, no new syntax is required to send a message to an object, beyond what was already described in Chapter 2.

REALbasic itself also comes with a large number of built-in procedures and functions apparently detached from any object; for example, in earlier chapters, we have already had cause to mention the MsgBox procedure, the Beep procedure, and the Str function. To call these, as we have seen, you just use their names; no dot notation is required. For the sake of completeness, though, and for a rigorous understanding of their ontological status, you might like to consider such built-in procedures and functions to be methods of a sort of supreme, ultimate, universal object--the global object.

A thing is global if it is available from everywhere without specifying an object that it belongs to; so, for example, since any code whatever can call the Beep procedure without using dot notation, the Beep procedure is global. So, we can pretend there is a universal object, which we might term REALbasicItself, and that when we say:

beep

it is really a kind of shorthand for:

REALbasicItself.beep // you can't actually say this!

This construct is only a pretense; there actually is no REALbasicItself object, and you can't actually send a message to it as in the second example. But there is a sense in which this pretense is accurate, and helps explain the status of REALbasic's built-in procedures and functions: when you call such a procedure or function, you're actually sending a message to REALbasic itself, and accessing one of its methods.

Object Design Philosophy

Methods and properties correspond to the two primary purposes of objects as programming constructs: encapsulation of functionality and maintenance of state. These two purposes underlie the design of object-oriented code, and an understanding of them will help you to make sense of REALbasic's built-in objects, to appreciate REALbasic's application architecture, to organize your code intelligently, and to know when to create your own objects.

Encapsulation of Functionality

The idea behind encapsulation of functionality is that each object should be the repository of knowledge about how to do all the things appropriate to itself.

Consider, for example, a shoot-'em-up arcade game, where every time the user hits a target, the target explodes, and the score, displayed in a box, increases. Now, we could write such a program without using object orientation at all; it's all just pixels on a screen, after all, and code that controls pixels has the same effect no matter how it's organized. But if we do use object orientation, the very language in which we describe the program, where the nouns are "target" and "score" and the verbs are "explodes" and "increases," suggests that the target and the box containing the score are two different objects, and that it is the target that should contain the code for exploding, and the scorebox that should contain the code for increasing. The target "knows" how to explode; the scorebox "knows" how to increase. In other words, the target object has an Explode method, and the scorebox object has an Increase method.

Messages can be sent to the appropriate objects to instigate the appropriate actions. Instead of exploding the target from elsewhere, we send the target the Explode message, as if to say: explode yourself! Instead of increasing the score from elsewhere, we send the scorebox the Increase message, as if to say: increase yourself! (In fact, our use of the conjunction "and" in describing the program's behavior suggests that it should probably be the target which, as it explodes, tells the scorebox to increase.)

When code is object-oriented, and when the objects encapsulate their appropriate functionality, the objects become more like objects in the real world. It is encapsulation of functionality that gives objects their autonomy. Only the target needs to know how to explode; all other objects can remain blissfully ignorant of the details, secure in the knowledge that they can just say target.explode and all the right things will happen. Moreover, as we develop the program, we can change what an explosion consists of (we can decide to add a sound, for instance) without affecting any code in any other objects; they still just say target.explode. Indeed, the target object may be so autonomous as to have virtually no dependency on the rest of the program; everything the target needs in order to function is inside itself. This makes it easier to write and maintain not only this program, but also any other programs that may need targets; to implement targets in another program, we have only to copy into that program our target object code from this one.

Maintenance of State

The idea behind maintenance of state is that a value needing to be preserved over time should be preserved as part of the object that chiefly operates on it.

Recall that our arcade game is to have a scorebox that knows how to increase itself, and observe that this implies there is a score. Even while the user is busy missing targets, or taking time out to answer the telephone, some object is remembering this score. It makes sense that this object should be the scorebox itself; the jobs of increasing the score, displaying the score, and knowing the score are naturally related. In other words, the scorebox object has a Score property.

Once again, this approach allows other objects to be ignorant of the details, and allows the scorebox to maintain autonomy. When the user hits a target, the score should increase. Let's say that when the user hits a different object (a bad guy), the score should also increase. Both the target object and the bad guy object react to being hit by telling the scorebox to increase itself: scorebox.increase. Neither has to worry about what this means, or what the score is, or how the user will be shown the score. The scorebox, in its turn, doesn't have to worry about who is telling it to increase the score. It just sits there, remembering the score, and when it's told to increase it, it does so, and it both remembers and displays the new score, which is simply the value of its Score property.

To be sure, this example is particularly clear-cut in a way that is sometimes not the case. Interesting questions of philosophy and implementation often arise. That's part of the fun, and the challenge, of designing an object-oriented program.

For example, the target object and the bad guy object don't need to know the score or manipulate it themselves; but what if they did? Should they be permitted to access the scorebox's Score property directly, or should they be required to call one of the scorebox's methods? In other words, is the scorebox not just this value's container, but also its owner and, in some sense, its protector?

Furthermore, it may not always be entirely obvious which is "the object that chiefly operates" upon a certain value. It often seems that direct access to a value needs to be shared among several different objects; should one of these be its container, or its owner and protector, or should the value be spun off into a different object entirely?

Then there is the problem of a value's life cycle. Clearly, if the scorebox object for some reason goes out of existence, yet we still need access to the score, the scorebox was an inappropriate choice of container for the score. At the other extreme, we could make the score permanently and publicly accessible and part of no object at all,[1] but this seems to defeat the purpose of object-orientation, if there is any object that seems naturally to be the score's owner.

These are all practical and philosophical design decisions with which the programmer must constantly grapple.

Summary

Figure 3-1 represents some of the features of objects we've just discussed: their autonomy, their ability to receive and send messages, their encapsulation of functionality, and their maintenance of state.

Functionality is expressed by methods. State is expressed by properties. Code that is well organized into objects with appropriate methods and properties has several virtues. It is:

Legible
When code in some object says target.explode, it's fairly obvious what it's meant to do.

Maintainable
If we decide to modify the code that explodes the target, we know where to find it, and code outside the object remains untouched.

Expandable
If we decide the target needs to be able to do something more, such as move to the left, we know where to put the code.

Portable
If we write another game that uses targets, we can just export our target object code from this program and import it into the other.

Figure 3-1. Objects doing their jobs autonomously

 

Classes and Instances

An object is an abstraction, a way of thinking and speaking. We come now to the details of how objects are actually implemented in the REALbasic programming environment. They appear in two forms: as classes and as instances. We begin with an explanation of what a class is.

Consider the notion that every object is of some type. The notion of types of object accords intuitively with our ideas of objects in the real world; these two pencils are two different objects, yet they are both pencils. Such a notion implies immediately that our program may have more than one object of the same type; for if this were not possible, and every object were the only one of its kind, the notion of types of object would be superfluous.

The possibility of our program containing more than one object of the same type has both a ready intuitive appeal and an obvious practical value. Returning to our arcade game example, imagine that the user is to be confronted with numerous targets marching across the screen. They look alike, and they behave alike (they explode when hit). It makes perfect sense that we should be able to say and think that each of these targets is a different object (only the one that is hit will explode), yet they are all targets (any one that is hit will explode in the same manner). The same is true of the scorebox; there might be two different scores (how many large targets the user has hit, and how many small targets), each of which is increased individually, and each of which is displayed in a separate box. It makes perfect sense that we should be able to maintain two different scores in two different objects, yet we should be able to say and think that both objects are scoreboxes (each contains a score that it knows how to display and increase).

Figure 3-2 illustrates this notion. The round things, which are marching across the window at the left, are targets. They are different objects; yet they all look identical, because they are all of the same type--each is a target.

Figure 3-2. A class and several instances of it

 

Furthermore, without the ability to implement multiple objects of the same type, objects threaten to become a hindrance to the organization of our code, rather than a benefit. Suppose our arcade game is to show the user dozens of targets, each of which can be sent the Explode message. If exploding is to be implemented the same way for every target, and yet that same code must be written separately in each target, imagine what this will mean both for writing the code originally and for changing it later on! The programmer would surely collapse in a state of boredom, frustration, or madness before the program was ever completed. Clearly, in order to do useful object-oriented programming at all, we must have a way to implement object types, and this implementation must include a facility for sharing code, so that all objects of the same type can be sent the same messages and can respond identically to them.

The programming construct that expresses the notion of an object type is called a class. Methods and properties that are to be in common for every object of a certain type are placed in the class that represents that type.

For example, if we have a Target class, then we write one Explode method, in the Target class. (And with REALbasic, when I say "in the Target class," I really mean in it; the Target class will be a listing in the Project Window, and we will double-click that listing to open a Code Editor that belongs just to the Target class, and that's where our code will go.) Now any and all targets in our program will be able to receive the Explode message and will know what to do in response. If we decide to change the way all targets respond to the Explode message (we decide to add a sound to the explosion), we change the code in just one place, the Explode handler in the Target class.

Similarly, if we have a ScoreBox class, then we write one Increase method, in the ScoreBox class, and we create one Score property, in the ScoreBox class. Now any and all scoreboxes in our program will maintain a score, and will know how to increase it.

However, there's an important distinction to be drawn here between the case of a method and the case of a property. For example, every scorebox will have an Increase method (because the ScoreBox class has one), and it will be the same method.[2] On the other hand, every scorebox will have a Score property (because the ScoreBox class has one), and its datatype will be the same (namely, the datatype declared for this property in the ScoreBox class), but its value will be separate, and separately maintained, for each scorebox. In other words, a property is declared in the class, but the individual objects in the program still maintain state individually, so each has its own value for that property. This makes intuitive sense if we consider an object such as a button: clearly any button has a location within its window, so it should have a Top property and a Left property, but we would not want all buttons to be forced to have the same location.

So, a class is an object type, with facilities for sharing code and property declarations among individual objects. Those individual objects, for their part, are called instances; and every object is said to be an instance of some class. In our arcade game, each target that the user sees represents a separate instance of the Target class, and each of the boxes showing a different score represents a separate instance of the ScoreBox class.

So, in Figure 3-2, each of the round things in the window on the left represents a separate instance of the Target class. As many targets as you see, that's how many instances of the Target class exist at this moment in the running of the program. There is just one Target class, on the other hand, and you can see it listed in the Project Window.

Whenever a message is sent, it is sent to some particular instance. Returning to Figure 3-1, we can see why this makes sense. A target is a type of object, and any target object needs to know how to explode; but at this moment we're sending the Explode message to this particular target. A scorebox is a type of object, and any scorebox object needs to maintain a score and to know how to increase it; but at this moment we're sending the Increase message to this particular scorebox so it will increase its particular score.[3]

So, in Figure 3-2, each separate round thing in the window on the left was drawn by sending the Draw message to a different instance of the Target class. Each of these different instances drew itself in the same way, though, because the knowledge of how to react to the Draw message is embedded in just one place, the Target class, shown on the right. The Code Window is the Target class's Code Window, and you can see the actual code that each target instance is using to draw itself at every moment.

It may appear at this point that the relationship between classes and instances borders upon the metaphysical. Anyone who knows anything about philosophy will be inclined to suspect that classes and instances are just another way of talking about universals and particulars. Indeed, object-oriented programming seems to fulfill Plato's philosophical program announced in the Euthyphro (6e):[4]

SOCRATES. Now, you recall that I asked you to explain to me, not this or that particular pious thing, but that Form Itself through which all pious things are pious? You did say, I believe, that it was through one Form that impious things are impious and pious things are pious; don't you remember?
EUTHYPHRO. Yes, I do.
SOCRATES. All right, then; so, explain to me what is this Form Itself, so that by keeping my eyes upon it and using it as a model, I may declare that whatever you or anyone else does that is of this sort, is pious, and that whatever is not, is not.

The problems with Plato's characterization are well known: the Form seems to be a "thing" separate from the particular things of the world around us, the notion "through" is crucial but slippery, and Plato seems to equivocate rather glibly between the Form's being responsible for a thing's being such and such and our ability to know that a thing is such and such; thus, his program is almost certainly doomed to failure as an explanation of how the world works. But he is perfectly accurate about how an object-oriented program works! If an instance is of the Pious type, there really is a separate Pious class that really is responsible for the instance being such as it is.

Fortunately, unlike Plato's Forms and particulars, there is nothing mysterious, metaphysical, or vague about the relationship between classes and instances. And REALbasic makes the relationship especially clear, and the distinction especially sharp, by locating classes and instances firmly in two different worlds (Plato would have been proud!), as follows.

In the IDE, every object you edit is a class. Every window listed in the Project Window and viewed in a Window Editor is a class. Every control whose icon is shown in the Tools Window is a class. Any new class you create is a class.

In your code, on the other hand, every object to which you send a message is an instance. In fact, your application, as it runs, consists of nothing but a lot of instances, sending messages to one another. And since REALbasic's built-in objects know how to portray themselves on the screen as interface elements, the interface of the running application may be thought of as being composed of instances. The window that appears as the application runs corresponds to an instance. The button contained by the window corresponds to an instance. The menu item you choose corresponds to an instance.

TIP:   The principle just enunciated is so important and so fundamental to one's ability to work in REALbasic with fluidity and understanding that I call it The Golden Rule of REALbasic. The following mantra serves to summarize the Golden Rule, and repeating it daily (preferably in front of a mirror, though this is not absolutely necessary) is guaranteed to bring enlightenment and to dispel the clouds of confusion: In the IDE, only classes. In the running application, only instances.

Figure 3-3 illustrates the Golden Rule. The programmer is shown editing classes; then the application is built and run, and the running application consists of instances, which can receive messages, and some of which are responsible for interacting with the user.

Figure 3-3. The Golden Rule of REALbasic

 

In Chapter 1 it was pointed out, in connection with properties, that editing in the IDE sets up the initial conditions of the application, but then, once the application has actually started running, the code takes over, and has the power to change those properties. A somewhat analogous observation may be made with respect to classes and instances. Editing in the IDE defines the classes for all the objects that will appear in the running application, but it is only as the application starts up, and as the application continues to run, that any instances of those classes are generated, so that messages may be sent to them, so that subroutines may be called, so that the application can actually do anything.

The reader, as programmer, will thus be seriously concerned with how instances are generated (so that there will be something to send a message to) and how they are referred to in code (so that one can specify which object the message is to be sent to). Before we come to this, though, we round out our discussion of classes by providing a road map of the internals of a class.

Anatomy of a Class

Back in Chapter 1, we learned our way around a Code Editor, and spoke loosely of a Code Editor as belonging to an object. Now we can be more rigorous. As we have just seen, what's edited in the IDE are classes. Thus, a Code Editor provides a view of what constitutes a class. Indeed, it is the best such view, since the other two views, the Properties Window and the Window Editor, show only properties. Not every detail of every class appears in a Code Editor (as I'll explain in a moment), but only in a Code Editor does the general anatomy of a class stand fully revealed.

In order to study the Code Editor of a class, we might wish to make a class. To do so, you choose File New Class, unless the class is to be a window class, in which case you choose File New Window. However, there is no need to do this right now; issues of class creation are the subject of Chapter 4, and it just so happens that even an empty project provides us with one excellent example of a ready-made class--the Window1 window class. So you might wish to open the Window1 Code Editor at this point, to help you follow the discussion.

A Code Editor reveals that a class consists of two sorts of things: handlers and properties. Handlers are containers for subroutines, as explained in Chapter 2. Properties, as explained in the previous section, are variable-like entities belonging to an object, which can be accessed through messages.

You may perhaps have expected, from the previous section, that a class would consist of methods and properties, corresponding to the two purposes of a class, encapsulation of functionality and maintenance of state. This is essentially right, but in REALbasic terminology, there are handlers that are not methods. The reason is that REALbasic organizes its object model to fit into its application framework in a particularly clear, well-defined way. Under this organization, methods are one kind of handler--the kind that can be called by the programmer, in code, by sending a message to an object. Other handlers in a Code Editor get called in a different way, namely, by REALbasic itself, as part of the application framework; I'll say more about this matter in a moment.

Handlers and properties are the only things a class consists of; together, they constitute its members. In the following list, I describe each of the different types of member, how they are classified, where (if anywhere) each type is displayed in the IDE, how they are created and edited, and how they are accessed when the application runs:[5]

Built-in methods of built-in classes
REALbasic includes many built-in classes, most of which already have some functionality, available to you through methods that your code can call by sending an appropriate message to an instance of the class. For example, a window knows how to make itself visible or invisible on the screen, and you can make a window instance do this by sending it the Show or Hide message. No trace of these methods is displayed in the IDE. To learn what methods of built-in classes exist and what they are for, consult the REALbasic documentation, or this book.

Built-in properties of built-in classes
REALbasic includes many built-in classes, most of which have some properties that your code can access by sending an appropriate message to an instance of the class. For example, a window has a Visible property, which you can set to make the window visible or invisible, and a Top property, which you can set to position the window vertically on the screen. Some built-in properties, but not all, are listed in the Properties Window, or are made manifest in the Window Editor, so that you can set their initial values in the IDE. For example, a window's Visible property appears in its Properties Window, and you can set its initial value there, and your code can send a window instance the Visible message. But a window's Top property does not appear in its Properties Window, and can be set only in code, by sending a window instance the Top message. For some built-in properties, your code can get the value, but cannot set it; these are known as read-only properties. Some read-only properties can be set in the IDE; others can't. To learn the purpose of built-in properties, and to know about those not listed in the Properties Window, consult the REALbasic documentation, or this book.

Controls
A window's Code Editor contains a Controls category. The entries here constitute a special case, because only a window class can contain controls. Controls are discussed later in this chapter. The handlers listed in a window's Code Editor as belonging to controls are actually event handlers, which we take up next.

Event handlers
Event handlers are listed in a Code Editor's Events category (and, if it is a window's Code Editor, in its Controls category). Event handler code, if there is to be any, must be written by the programmer. But the event handlers themselves are created only by REALbasic; the programmer cannot add or remove them. Your code cannot call any event handlers; they are called exclusively by REALbasic itself, when an event of the appropriate type occurs.[6] Event handlers are the primary way in which your code learns what the user is doing, and gets to react. For example, a button's Action event handler code, if the programmer has written any, is called by REALbasic when the user clicks that button. To learn what events trigger what event handlers, consult the REALbasic documentation, or this book.

Menu event handlers
Menu event handlers are listed in a Code Editor's Menu Handlers category. Menu event handlers are created by the programmer, by choosing Edit New Menu Handler; the programmer also writes their code. In essence, a menu event handler is a specialized kind of event handler. The programmer can create or delete menu event handlers unlike an ordinary event handler. But, like an ordinary event handler, a menu event handler cannot be called by the programmer's code; REALbasic calls it in response to the user choosing a menu item.

Method handlers
Method handlers are listed in a Code Editor's Methods category. Method handlers are created by the programmer, by choosing Edit New Method; the programmer also writes their code. Method handlers may be called only by code the programmer has written. This is done by sending an appropriate message to an instance of the class.

Programmer-defined properties
Properties defined by the programmer are listed in a Code Editor's Properties category. They are created by the programmer, by choosing Edit New Property. This brings up a dialog in which the property is declared; the declaration is formally identical to a one-item Dim statement without the Dim keyword--for example, score as integer. The dialog is pictured in Figure 3-4. Property declarations can subsequently be edited by double-clicking the property's listing in the Code Editor (or select the listing and choose Edit Edit), which brings up the same dialog. A programmer-defined property's value is autoinitialized in exactly the same manner as a variable: that is, when the instance is generated, the property's value is a form of zero. The programmer's code then accesses the value of a property by sending an appropriate message to an instance of the class.

Figure 3-4. The dialog for editing property declarations

 

The purpose of the Private checkbox depicted in Figure 3-4 is explained in Chapter 4. The Visible box is useful only under very limited circumstances: if it is checked, and if the class is not a subclass of the Control class, and if an instance of the class is contained in a window, the property is listed in that instance's Properties Window. The fact that the Visible box is always present, even when it does nothing, should probably be regarded as a bug.

An Instance Is Born

Instances are literally the only things that allow an application to act. Without instances, there will be nothing to send messages to, no methods or event handlers will ever be called, and none of our code will ever run. Remember the Golden Rule, from earlier in this chapter? The objects in the built application's running code are all instances. But the only thing that can be edited in the IDE are classes. So, in the running application, where do instances come from?

Since every instance is an instance of some class, the question amounts to this: how to make an instance of a given class. Another way of putting it is to use the technical verb: we wish to know how to instantiate a given class.

This question will now at last be answered. There are five different cases.

The Application Class

As your application starts up, the Application class (or a subclass of it, if you've created one) is instantiated once, automatically. This single instance persists throughout the lifetime of the application. You cannot instantiate the Application class (or its subclass) in code.

Because an instance of it is absolutely guaranteed to exist uniquely and at all times, the Application class is an appropriate repository for methods and properties that are to be available always from anywhere (globally). Global methods and properties are taken up in Chapter 4. Other features of the Application class are discussed in Chapter 7.

The Default Window Class

Every application has either one default window class or none. To determine which is the default window class, choose Edit Project Settings and, in the resulting dialog, use the Default Window popup; you can specify any existing window class, or "<none>".

If there is a default window class, an instance of it is generated automatically as the application starts up, just after the Application class is instantiated. This also instantiates the controls within the window, as we shall see. There is nothing special about the default window instance; it's just a convenience so your application has something to get started with, if you like. Nor does the default window class behave specially in any other way. Your code can generate further instances of it, and any instance of it can be destroyed, just as with any window class, by closing the window.

Control Classes

Controls are instances contained by windows. Basically, the rule is that you cannot instantiate a control in code, and you don't need to worry about doing so: a window will automatically instantiate all the controls it contains, when it itself is instantiated. (The rule is actually slightly more complicated than this; as we shall see later in this chapter, once a control has been instantiated by its containing window, the instance can be cloned to make another instance.)

Menu Items

Items in menus at the top of the screen are instances of the MenuItem class (or of a subclass of the MenuItem class). You cannot instantiate a MenuItem; instead, you set up your MenuItems in the Menu Editor, and they are automatically instantiated as your application starts up. (As with controls, I am deliberately ignoring for the present the matter of cloning MenuItems.) MenuItems are discussed in Chapter 6.

Noncontrol Classes

If an instance is not contained in a window, is not a MenuItem, is not the Application class instance, and is not the initial default window class instance, it does not exist until your code in the running application explicitly generates it. This is done in one of two ways.

One way is for your code to call a function that creates and returns an instance of the class. Quite a number of such functions are provided. For example, the GetFolderItem function takes a file's pathname, expressed as a string, and returns an instance of the FolderItem class that corresponds to that file; so, given a file myFile in a folder myFolder on a disk myDisk, we might say:

dim f as folderItem
f = getFolderItem("myDisk:myFolder:myFile")

Since GetFolderItem is a function returning a FolderItem, we must call it in a context where a value of type FolderItem is expected. In this example we have assigned the value to a variable declared as being of this type.

The second way to make a new instance is with the New operator. This operator must be accompanied by the name of the class of which a new instance is to be made; it instantiates the class and returns the instance just as a function does. Let's suppose your project has a window class, MyOtherWindow. Then you could say:

dim w as myOtherWindow
w = new myOtherWindow

This causes an instance of the MyOtherWindow class to be generated; typically, indeed, a MyOtherWindow window will visibly appear in the running application.

Like a function, the New operator returns a value, so it must be used in a context where a value of the particular class type is expected. In this example, we have assigned it to a variable declared as being of this type.[7]

In the case of window classes only, there is a third way to make an instance, discussed in the next section.

Referring to Instances

Once your application is running and some instances have been generated, much of the action will be precipitated by your code calling methods, and getting and setting properties--in other words, by sending messages to instances.

Many of REALbasic's built-in methods, as was pointed out earlier, are global, but many of them belong to some particular class, and the only way you can call these is by sending a message to an instance of that class. The same is true for methods that you write: you can write global methods (as will be explained in Chapter 4), but for the most part, in line with the considerations presented in "Object Design Philosophy" earlier in this chapter, you'll write them as method handlers in some class, and the only way you can call such a method is by sending a message to an instance of that class.

Exactly the same thing is true for properties. Many of REALbasic's built-in classes have properties, and you will often need to retrieve the value of such properties, as a way of obtaining information; for example, getting a window's Top and Left properties tells you where it is on the screen. You'll also frequently want to change the value of such properties; for example, setting a window's Top and Left properties is how you move the window. And of course you'll frequently have properties you've created yourself, and though you can make global properties, more often you'll declare the property in some particular class, and then you'll want to get or set the property's value, in code, and the way you get and set a class's property in code is by sending a message to an instance of that class.

Now, we already know the syntax by which your code will send a message to an instance--dot notation. As explained earlier in this chapter, what appears after the dot will be the name of a property or method, and the syntax of the dot notation expression as a whole is the same as that of a variable name or a subroutine call.

But what about what comes before the dot? This is where you tell REALbasic what instance to send the message to. You do this by providing a reference to the desired instance--a name or other expression that specifies the instance in question.

The ability to provide a reference to an instance is thus of crucial importance. To repeat: In REALbasic's object-oriented programming environment, making things happen is largely a matter of sending messages to instances. If you can't send a message to an instance, you can't make any of its method handlers execute, or get or set any of its properties. And you can't send a message to an instance if you can't say what instance it is. Thus, it is crucial to know how to refer, in code, to any desired instance. It is not too much to say (and I do often say it) that 99% of the art of writing REALbasic code lies in knowing how to obtain a reference to an instance. That's what the rest of this chapter is about, so pay attention!

Being able to provide a reference to an instance is important for another reason: an instance with no way to refer to it is useless and in fact will cease to exist; see "Destruction of Instances," later in this chapter. For certain instances, REALbasic sees to it that there is always a reference, and therefore that the instance will persist--in particular, with the Application instance and the MenuItem instances, and for windows and their controls. But otherwise it is up to the programmer to see to it that there is some way for an instance, once generated, to be referred to, for as long as it is needed.

One way to be perfectly certain of this is to maintain a name, so let's talk about that first.

Maintaining a Name

A very typical way to refer to an instance in code is to use the name of a variable or property to which that instance has been assigned. Once such an assignment has been made, then as long as that variable or property persists, and as long as some other value is not assigned to the variable or property, this name can be used as a reference to the instance.

For example, recall from the previous section that a FolderItem instance was generated by saying:

dim f as folderItem
f = getFolderItem("myDisk:myFolder:myFile")

After this assignment, f refers to a FolderItem instance, and can be sent messages appropriate to a FolderItem. For example, a FolderItem has a Delete method, so we could say:

f.delete

This would send the Delete message to the FolderItem instance to which f refers, which would respond by executing the FolderItem class's Delete method (which happens to mean that it would delete a file, though for the purpose of the example that's unimportant).

Let's do it again, for good measure. Recall that earlier we generated a window class instance by saying:

dim w as myOtherWindow
w = new myOtherWindow

Afterward, w refers to the instance just generated, and we can use w to keep referring to this same window instance, sending it messages such as these:

w.top = 50
w.title = "This is fun!"

A variable's scope and lifetime are very limited, though. Variables stop existing when their subroutine finishes executing, and they can be referred to only from within that subroutine. If we wish to maintain a reference to an instance for longer than a single subroutine, and to be able to use that reference from elsewhere, we may prefer to assign the instance to a property; a property is itself part of an instance of a class, and persists as long as that instance does. (If this gives you a vision of instances stored in properties stored in instances stored in properties stored in instances, you're absolutely right.)

Be warned: merely declaring a variable or property has nothing to do with instances! It doesn't generate an instance, and it doesn't make a reference to an instance. Thus, just after this Dim statement, f is not a reference to a FolderItem at all:

dim f as folderItem

The declaration says that the variable f is to be of the FolderItem type, but until you have actually gotten hold of an actual FolderItem instance and assigned this FolderItem instance as a value to f, f does not refer to a FolderItem instance and must not be sent any messages. If you were to say this:

dim f as folderItem
f.delete // oh, no! error: NilObjectException

an exception would be raised, meaning that your application would terminate prematurely (unless you are prepared to catch the exception, as described in Chapter 8). I will have more to say on this topic later in the chapter.

Functions as References

A number of built-in REALbasic functions return an instance. Also, it is possible for you to write such a function. Since the result of a function is substituted for the function call, the function call itself is a reference to an instance. Under certain circumstances, that will be the only reference you need.

For example, on rare occasions, an instance is maintained so briefly that it doesn't require a name at all. Consider the task of deleting a file on disk given its pathname. We could say:

dim f as folderItem
f = getFolderItem("myDisk:myFolder:myFile")
f.delete

But if all we wish to do with this file is delete it, there is no need for such elaborate measures; we're not going to refer to this FolderItem ever again, so the name f is not required later on, and it certainly isn't required as an intermediary just so we can send an instance the Delete message. Rather, we can create the instance and send it the Delete message all in one line; we lose all ability to refer to the instance immediately afterward, but we don't care:

getFolderItem("myDisk:myFolder:myFile").delete

Windows are another good example of where it can be easier to get a reference as the result of a function. It happens that REALbasic maintains a list of window instances for us, and we can consult this list by calling the Window function. To do so, we hand the Window function the index of the window we want, where the index number corresponds to the layering order of windows from front to back (that is, window(0) is the frontmost window). This is useful particularly when the order of windows is what we are concerned with. For example, let's suppose that our application has three windows showing. And suppose we have a menu item, Toggle Front Two, which brings the second window to the front. How can we know which is the second window? We could maintain a name for each window and keep a list of the windows in order, changing the list each time the user brings a different window to the front. But this would be tedious and a waste of effort, because the Window function already exists. To get a reference to the second window, it is sufficient to speak of window(1). Since the Show method brings a window to the front, the way to bring the second window to the front is simply to say:

window(1).show

Paradoxically, sometimes we are forced to set aside a name for an instance even though we don't need it. For example, the Window function (and the Self function and the global instance name, which we shall come to in a moment) may be enough to ensure that we will be able to obtain a reference to a window without maintaining a name for it. But all the same, we are often compelled, when we wish to instantiate a window class using New, to assign the result to a name anyway! That's because we cannot say simply:

new myOtherWindow // error: Statement expected

The New operator returns a result, and we must do something with that result. The simplest option is to assign that result to a variable; but this means we must go to all the trouble of declaring that variable beforehand and performing the assignment even though we may have no intention of using this variable ever again:

dim w as myOtherWindow
w = new myOtherWindow // and that may be the last we'll ever hear of w

Self

Code can obtain a reference to the instance that is actually running that code by way of the Self function. It may not be obvious to you what class this will be an instance of, so just follow this rule: in whatever class's Code Editor the Self function appears, the result of Self will be an instance of that class. It may also not be obvious to you what instance of that class will be running the code (unless there is just one instance of the class, of course); but that's exactly why use of the Self function is such an important technique--the programmer, writing code in a class, may have no idea what instance will actually execute that code and when, and doesn't need to know.

For example, suppose that, in our arcade game, when a target receives the Explode message, one of the things it ultimately does is to vanish, and that the way it does this is by setting its own Visible property to false. The programmer, writing the Explode method handler in the Target class, therefore wants to set this Target instance's Visible property to false. But which instance is that? The programmer has no notion which target the user will hit, or even of how many targets there may be at the time; how can the message be sent to the correct Target instance? Use of the Self function solves the problem:

self.visible = false

That line appears within the Target class's Code Editor, so Self means a Target instance. In particular, it means the Target instance that actually executes the line. Since the line appears in the Target class's Explode method, the instance is the Target instance that is running the Explode method. But the only way a method can be executing is if an instance is sent a message that calls it. So the Target instance running this code is obviously the Target instance to which the Explode message was sent. And that's the very instance that should vanish.

For an event handler of a control in a window, the rule about the Code Editor means that Self returns a reference to the window instance. This means that Self has a second use: it acts as a line of communication between a control and the window that contains it. If you drag a PushButton from the Tools Window into a Window Editor, and make its Action event handler go like this:

msgBox str(self.width)

and run the project and press the button, the dialog will display the width of the window, not the button. The button's Action event handler is edited in the window's Code Editor, so Self means the window. (To access the button's Top, you could say me.top; see "Me," later in this chapter.)

When sending a message to the instance returned by Self, it is permitted, as a kind of shorthand, to omit self altogether. In this syntax, only the name of the message appears. (Personally, I must admit to wishing that this shorthand didn't exist. In my opinion, it only confuses things: it makes code harder to read, and it puzzles beginners, who can't figure out why sometimes they can access a certain property, control, or method and sometimes they can't.)

This explains the technique outlined in Chapter 2, where the reader was invited to test the use of subroutines by calling a window's method handler from a button's Action event handler by name alone. On , when we said msgbox str(average(3,5)), this use of average was really a shorthand for self.average; we were actually sending a message to Self, the window instance containing the button.

In order to understand how it can be possible to omit self, you need to know how REALbasic resolves names. So that's the next topic.

Resolution of Names

REALbasic figures out what you mean when you use a name by searching the namespaces in a definite order:

  1. If the context is that of a method call, REALbasic looks among its own built-in global methods.
  2. If the context is that of a variable name, REALbasic looks among the local variables.
  3. REALbasic looks among the members of the class of which an instance would be returned by Self.
  4. REALbasic looks among any global methods or properties that you've defined in a module, as explained in Chapter 4.

WARNING:   REAL Software, Inc., have asked me to warn you that the name resolution order is officially undocumented and therefore subject to change. I'm glad of this, since it is a very peculiar order, and has some confusing ramifications. My purpose in this section is not to encourage you to take any sort of deliberate advantage of the name resolution order, but rather to point out some of the problems it can cause you.

You can define a local variable called msgbox, but you can never refer to it, because msgbox always means the global method. On the other hand, if you define a method handler called MsgBox in a window, you can refer to it; msgbox still means the global method, but self.msgbox means the method handler. But built-in properties (and built-in constants) behave quite differently. A window has a built-in Top property; you can define a variable called Top in window code and still refer to it separately from the built-in Top property:

dim top as integer
top = 0 // sets the variable
self.top = 40 // sets the built-in property

On the other hand, if you create a window property Top, you can never refer to the window's built-in Top property; it becomes absolutely unavailable, and that's bad because now you've killed a piece of REALbasic's built-in functionality (you can't move the window vertically, or find out where it is). The fact that REALbasic doesn't prevent you from declaring a property that overrides a built-in property of a class is probably to be considered a bug--especially since REALbasic does prevent you from declaring a method handler that overrides a built-in method of a class.

Referring to Controls

A control instance is generated automatically by the instantiation of its containing window, so you are given no chance to capture a reference to it as a name. Therefore, it has a name already--the value of its Name property in the Properties Window. We may speak of this as the control instance's global name. The instance can be referred to in any context, by chaining its global name to a reference to the window, using dot notation:

theWindow.theControl.theMessage

Since code within a window's Code Editor can obtain a reference to the window using the Self function, the Self function enables such code to refer to any control within the same window. For example, suppose a window contains two PushButton instances, named PushButton1 and PushButton2, and we wish the first, when pushed, to disable the second (by setting its Enabled property to false). The Pushbutton1 Action event handler (in the window's Code Editor) can say:

self.pushButton2.enabled = false

Furthermore, self as a message recipient can be omitted; therefore, in actuality it suffices to say:

pushButton2.enabled = false

But to speak of a control in a different window, we need an explicit reference to the window:

isEnabled = w.pushButton2.enabled

A completely different way to obtain a reference to a control in a window is through the window's Control function. The parameter is a zero-based index value specifying the desired control (the order of the controls is the order set in the Format Control Order dialog). To learn the upper bound of this parameter, subtract 1 from the window's ControlCount property. For example, the following routine interrogates a window to learn how many of its controls are instances of PushButton (or subclasses thereof):[8]

dim  i, j, u as integer
u = myOtherWindow.controlCount - 1
for i = 0 to u
    if myOtherWindow.control(i) isa pushButton then
        j = j + 1
    end
next
msgbox "MyOtherWindow has " + str(j) + " buttons."

Me

Code within a control's event handlers in a window class's Code Editor can obtain a reference to the control instance that is actually running that code by way of the Me function (not to be confused with the Self function, which refers, in such code, to the containing window).

As far as I can tell, the Me function is merely a convenience; it makes code easier to read and write, but it doesn't do anything for you that you couldn't already do. Every control instance already has a unique name, and there is no possibility that you're not going to know this name, so you could always have gotten a reference to the control without Me.

For example, let's suppose that we have some code inside PushButton1's Action event handler that moves PushButton2 to a position underneath PushButton1. Then we can say (omitting self for the sake of brevity):

pushButton2.left = pushButton1.left
pushButton2.top = pushButton1.top + pushButton1.height + 2

There isn't really any problem with this; but it seems sort of silly for PushButton1 to be talking about itself as pushButton1, and it makes it less obvious what the code is supposed to do. This is clearer:

pushButton2.left = me.left
pushButton2.top = me.top + me.height + 2

Also, you could then later change the name of PushButton1 or copy-and-paste this code into a different button, and this code would still successfully refer to it.

Me and Self differ only within the event handlers of a control in a window's Code Editor. In other contexts, they may be used interchangeably. It is not, however, permitted to omit me in the way that one may omit self. Indeed, it would be a mistake to do so, because in the situation where Me and Self differ, REALbasic will assume that what's omitted is self, which means the window, not the control. For example, suppose PushButton1 says:

pushButton2.top = top + height + 2 // oops

PushButton2 promptly vanishes, because it has just been placed lower than the bottom edge of the window.

The Window Global Instance Name

Window instances can be referred to in a special way: you use the window's class name as an instance name. You may do this for only one instance of each window class (obviously, since you could not use the same name to refer to more than one instance!). We shall call this name, which is the same as the window's class name, its global instance name. So, for example, if the default window class of your project is Window1, you can refer to one instance of it as window1.

The global instance name may seem like a strange thing: you're abusing the syntax of the language, speaking of an instance by using the name of a class. However, the device is actually rather a clever one. Window instances persist even if you have not maintained a name to refer to them (see "Maintaining a Name," earlier in this chapter); through the global instance name, all your window classes can have one instance to which nevertheless you always possess a reference.

There is a tricky rule associated with using global instance names. The way you instantiate a window class so as to be able to refer to that instance using the global instance name is not to use New. Instead, you just refer to the instance by its global instance name even though the instance doesn't exist. This doesn't cause an error; rather, the window class will automatically be instantiated, and your reference starts working immediately as a way of speaking of this new instance. In other words: whenever you use a window's global instance name at a time when no instance by that name exists, an instance by that name is generated. This is called implicit instantiation.

So, for example, let's say your project has a window class, MyOtherWindow, and let's say that no implicitly generated instance of MyOtherWindow exists. Then you can say:

myOtherWindow.title = "Info"
myOtherWindow.left = 20

The first line instantiates MyOtherWindow, assigns the new instance the global instance name myOtherWindow, and sets the value of the new instance's Title property, all in one move. The second line sets the value of the same instance's Left property; it doesn't implicitly generate a new instance of MyOtherWindow, because an instance with the global instance name already exists (the one generated by the first line).

Now, on the one hand, it's clear enough why implicit instantiation exists. If it didn't, the global instance name would be useless, because how would you (or REALbasic) know which instance of this window class the global instance name refers to? With implicit instantiation, it is possible to be very clear about this, because at every moment either there is or there isn't an instance of the window class to which the global instance name refers. If there is, that's the one. If there isn't, a new instance of the window class is generated, and this new instance becomes the one.

But although implicit instantiation may be explicable, it still tricks many REALbasic beginners--and not-so-beginners. Misuse of the syntax, which is all too easy, will cause a window instance to be created accidentally. Moreover, if there is already an instance of this window class, the new window may be created directly on top of the old one, so that the programmer, running and debugging the application, thinks it is the old one; an extra window has been created, and the programmer doesn't even know it. This is the source of many strange phenomena.

For example, let's suppose that the window class MyOtherWindow contains a button PushButton1. And let's suppose that you say:

dim w as myOtherWindow
w = new myOtherWindow
myOtherWindow.title = "Hello" // oops
w.pushbutton1.caption = "Press Me"

The window appears, and its titlebar changes, but the button's caption doesn't change. What's gone wrong? Nothing; but what you said isn't what you meant. Here are the consequences of this code. First, an instance of the MyOtherWindow class is created, using New, and a window appears; that instance is w. Then, a second instance of the MyOtherWindow class is created, using implicit instantiation, so a second window appears, hiding the first; that instance is myOtherWindow. The second window's title is changed to Hello. Finally, the first window's button's caption is changed to Press Me, but you can't see this, because the second window is in the way!

A more subtle error is to use the global instance name where a class name was intended. Suppose, for example, you wish to know whether the frontmost window is an instance of the MyOtherWindow class. You can get a reference to the frontmost window as window(0), so you might be tempted to say:

if window(0) = myOtherWindow then // oops

If there is no instance of MyOtherWindow with the global instance name, this expression will generate one, by implicit instantiation. The problem is that you asked the wrong question; you asked whether two instances were the same instance. What you wanted to know was:

if window(0) isA myOtherWindow then

Now myOtherWindow is just a class name, because IsA takes a class name; and no implicit instantiation takes place.[9]

A window instantiated by using New does not receive the global instance name, regardless of what name you use to maintain a reference to it. Suppose that a subroutine were to create an instance of the MyOtherWindow class in this way:

dim myOtherWindow as myOtherWindow
myOtherWindow = new myOtherWindow

The window thus created does not receive the global instance name, because implicit instantiation was not used. It's true that the name myOtherWindow refers to the window just created, but it's still just a name that you're maintaining (here, it's a local variable name). So if another subroutine were later to say:

myOtherWindow.pushbutton1.caption = "Press me"

this would create a second window, by implicit instantiation, rather than referring to the first.

The default window class is a special case. If your project has a default window class, then when that window is automatically instantiated as the application starts up, that instance is assigned the global instance name. So if your default window class is Window1, and if the automatically created window is still present, then when you say:

window1.top = 100

no new window is created; the default window is moved.

The Truth About Controls

The term control is used in two ways in REALbasic. In general, a control is an instance that is contained in a window. The window automatically generates the instance, when the window itself is instantiated. You can make a control from any programmer-defined class (except a window class), by dragging the class's listing from the Project Window into a Window Editor.

There is also a built-in Control class; an instance of a subclass of the Control class can exist only as a control (that is, it must be contained in a window). REALbasic supplies many built-in Control subclasses; all of these are represented by icons in the Tools Window. A few icons in the Tools Window also represent built-in classes that are not Control subclasses. You can make a control from any class represented in the Tools Window, by dragging its icon into a Window Editor.

When you make a control by dragging into a Window Editor, what are you really doing? Let's suppose that what you drag is the PushButton icon from the Tools Window. This icon represents a class--the built-in PushButton class. In dragging it into a Window Editor, you are not making a new class, because the PushButton class already exists; but you cannot be generating an instance, either, because what you edit in the IDE are classes. What you're actually doing is editing the window class, telling it to contain an instance of the PushButton class. Let's suppose this is the Window1 Window Editor. You're saying to the Window1 window class: "I want there to be a control that is an instance of the PushButton class. This instance is going to be your responsibility, Window1! You contain it. So, part of your job is to instantiate the PushButton class whenever you yourself are instantiated."

This makes perfect sense, because it captures our intuitive notion of window types. Let's say you're writing an email application, and one of your window types is for composing a new email message, and has a Send button in the upper right. You want the user to be able to work on several new messages at once. Therefore, more than one of these email composition windows may have to be open; in object terms, there will have to be more than one instance of this window class. So each of those windows should have a Send button in the upper right, because they are the same type of window.

That is precisely what will happen. You have one window class representing this type of window. You use the Window Editor to help describe this class to REALbasic. Part of the way you do this is to drag a PushButton icon from the Tools Window into this window class's Window Editor. This lets REALbasic know that the window is to contain a PushButton instance. You also use the IDE to describe the PushButton instance's initial properties. For example, you might dictate that its Caption property should be "Send". And the position and dimensions of the button's representation in the Window Editor also set the initial values of the instance's Top, Left, Width, and Height properties. Now each time a window of that class is instantiated in your running application, it instantiates a PushButton and initializes its properties. Thus, each window of this type that actually appears when your application runs will contain a button, at that position, with those dimensions, and with the caption Send.

Nor is it just a matter of how the Send button will look; there's also the matter of how it will behave. If you give the Send button some functionality in its Action event handler in the window's Code Editor, you expect that the Send buttons in all instances of this window type will exhibit this same functionality. And so they do. But why? Not because you've edited the PushButton class; after all, not every PushButton does this. Nor because you've created and edited a PushButton subclass; you haven't. It's because in editing the button's Action event handler within the window's Code Editor, it's the window class's code that you're really editing. You're saying to the window class: "Oh, and one more thing: when the user clicks on that PushButton instance you created, here's the code that should be executed."

This explains the fact noted in Chapter 1, that when you double-click a control in a Window Editor to edit its code, it is the window's Code Editor that opens. It also explains why, in this code, Self refers to the window, as described earlier in this chapter.

Control Clones and Control Arrays

There is usually no need for you to instantiate any controls in code, because you have already effectively instantiated beforehand all the controls you're going to need, by dragging them into a Window Editor. The window will instantiate them for you, when it itself is instantiated. That's what it means for an instance to be a control, and what it means for it to be contained in a window.

Nonetheless, you can instantiate a control in code, provided that an instance of that control exists in that window already (ultimately because you dragged it into a Window Editor). In other words, your code, although it cannot create an instance of a control ex nihilo, can clone an existing control. The values of properties can differ from clone to clone (for example, different clones of one button can have different positions in the window), but their event handlers in the window's Code Editor are shared.

You don't have to wait to do your cloning until the application runs; you can clone a control manually, in the IDE. We'll see in a moment why you might want to do this. But first, let's pause to discuss the matter of dynamic creation and destruction of controls.

Some newcomers to REALbasic are disappointed at its inability to create controls dynamically that are not clones. But this limitation is not as serious as it may appear, because no rule says that the existing control, the one that is to be cloned, must be visible. In other words, you can't create a control ex nihilo while the application is running, but you can certainly make it seem as if you did. As far as the user is concerned, a button can be made to appear in a window where none was previously, and that's all that matters. The truth is that this button existed already, but it was invisible, or was located outside the boundaries of the window. In fact, a common technique is to avoid cloning controls in code altogether, through the use of invisible controls. For example, if you know that you will need up to 10 buttons, you can create 10 buttons in the IDE and make them invisible. (If they will all have the same functionality, they can be clones of a single button, or instances of a single button subclass.) When the application runs, if it needs to present one of these buttons to the user, it just makes it visible.

By the same token, you can't get rid of a clone dynamically when you're done with it--because a control in general cannot be destroyed dynamically.[10] Again, as a workaround, you might consider a static set of clones whose items become visible or invisible as necessary. Or, instead of cloning controls at all, you might consider structuring your interface so that each control appears in a separate window; a window can be closed, and this destroys both the window and its controls.

REALbasic assists you to make controls visible and invisible en masse; see , and Chapter 18.

Control Arrays

Not just any control can be cloned. To clone a control, the control must be a member of a control array.

A control array is a construct for referring to a set of control clones numerically. It is not quite the same as an array of controls. An array of controls would be an array whose elements can refer to any control instances of the appropriate type; a control array consists of all the clones of a single control within a single window.

The notation for referring to the various clones within a control array is just like the notation for referring to the elements of any array: you use the array name and an index number, which is zero-based. The array name is the control's global name. For example, there might be three clones of a PushButton instance. The name of the control array is the global name of the original control, which might be pushButton1. The individual clones would then typically be pushButton1(0), pushButton1(1), and pushButton1(2). These names can be used just like any control's global name. For example:

window1.pushButton1(2).caption = "Delete"

Control arrays lack the functions of normal arrays. You cannot learn their size with Ubound. Also, you cannot use Insert, Append, or Remove on a control array. The really bad part is that you cannot pass a control array as a parameter to a subroutine: you can pass a control (including a control that is part of a control array), and you can pass an array, but you can't pass a control array. This situation becomes a genuine hindrance if we wish to cycle through several different control arrays, doing the same thing to the members of each of them;[11] there is no general way of doing so, and I regard this fact as a bug.

Nevertheless, control arrays have four great advantages. First, a control array makes it easy for your code to get a reference to a dynamically cloned control. For example, an original button, instantiated automatically by its window, has a global name; it might be pushButton1 (or whatever name you assigned it in the Properties Window). But without control arrays, a clone of this button generated by your code would have no global name in and of itself; it would be up to you to maintain another name that refers to it.

The second advantage of a control array is that, just like any array, it lets you refer to controls by index number, which is valuable in contexts where arithmetic most conveniently specifies the desired control. To repeat the example from Chapter 2, suppose we are playing a game involving a four-by-four grid; the squares where pieces can go might be represented by Canvas control instances. These Canvas controls are all going to behave identically, so they can be clones of one another. Thus they constitute a control array, and can be referred to as myCanvas(1), myCanvas(2), and so forth up to the 16th square, which is numbered myCanvas(0). This means that the square vertically beneath myCanvas(n) is:

myCanvas((n+4) mod 16)

The game grid is a good example of why you'd clone a control in the IDE. You know how many myCanvas clones you're going to need, so there is no point generating them dynamically in code; you're creating the clones just to take advantage of the power of control arrays.

The third advantage of control arrays is that they are self-initializing. With an ordinary array, your code must declare the array in some persistent storage, and then initialize it. If this were an array of controls, say an array of 16 Canvas controls, this would involve setting the first element to refer to the first Canvas, the second element to refer to the second Canvas, and so forth. With a control array, the global names myCanvas(0), myCanvas(1), and so on, automatically exist, and automatically refer to the clones.

The fourth advantage of a control array is that a control in a control array knows its own index number within the array. Such a thing would require a lot of work with an ordinary array, but with a control array, it's simply handed to you. This means that even though the code of clones is shared, the actual functionality of each clone can differ depending on the index. That's very powerful. In our game grid, for example, a square represented by a Canvas in a control array can identify the square beneath itself because it knows its own index.

There are two ways to learn the index number of a control that is part of a control array. First, if the code is in one of the control's event handlers in a window's Code Editor, it receives its own index as a subroutine parameter named Index. (This also works for menu event handlers, discussed in Chapter 6.) To take a trivial example, consider a button whose Action event handler contains this code:

dim i, u as integer
u = index + 1 // index is handed to us as a parameter
for i = 1 to u
    beep
next

Three clones of this button as a control array (let's say they are pushButton1(0), pushButton1(1), and pushButton1(2)) would behave differently when pressed: the first would beep once, the second would beep twice, the third would beep three times.

In all other situations, you use a second way to learn the index of a control within its control array, which is through its Index property. (In actual fact, every control has an Index property, whether it is part of a control array or not; but if it isn't, its Index value is an extremely unlikely number, the integer equivalent of negative infinity.) So, if we have a reference to a control within a control array, as (let's say) someControl, its index within the array is someControl.index. For example, the following code looks for every button in the same window that is part of a control array and changes its caption to reveal its index number:

dim i, u as integer
u = self.controlcount - 1
for i = 0 to u
    if self.control(i) isA pushbutton then
        if self.control(i).index >= 0 then
            pushbutton(self.control(i)).caption = str(self.control(i).index)
        end
    end
next

(The use of pushbutton() as what looks like a function call is actually a cast, explained in Chapter 4.)

Inside a control's event handlers in a window's Code Editor, code can get a reference to the control itself as Me (see the earlier section, "Me"). When the control is part of a control array, Me doesn't refer to the whole control array, but to the particular control (within the control array) that's running the code. In this example, we again assume pushButton1(0), pushButton1(1), and pushButton1(2); pushing any of the buttons moves the other two buttons to align their left sides with the left side of the button just pushed:

dim i as integer
while pushbutton1(i) <> nil
    pushbutton1(i).left = me.left
    i = i + 1
wend

In the case of a control that belongs to a programmer-defined control subclass (discussed in Chapter 4), the code in the subclass's Code Editor can get a reference to the particular control (within the control array) that's running the code as Self--or Me, but only because this is identical to Self in this context. Thus, self and self.index provide all the necessary information here.

By the way, you might be wondering about the nil test in the preceding example. This illustrates a useful device. You can't use Ubound on a control array, so if you are creating clones dynamically, how can you know the size of the control array at any given moment? One way is to maintain your own count. However, there is another way.[12] It turns out that there is no bounds checking on a control array; thus, there is no penalty for speaking of an array index for which no clone exists. Therefore, you can iterate up the array to find the first index that is a nil pointer. For example:

dim i as integer
while pushButton1(i) <> nil
    i = i + 1
wend

Afterward, i holds the number of clones in the control array. (For nil, see the next section of this chapter, "Being Careful with Instance References.")

Cloning in the IDE

To create a control array, select a control in a Window Editor and, in the Properties Window, assign a value to its Index property. Any legal index value will do, but you will almost certainly use 0. Each new clone is assigned the lowest array index available at the time it is created. That's why it's best to assign the original element the index 0, so that the next clone will have index 1, the next will have index 2, and so forth.

If you wish to create further elements of the control array in the IDE, just copy and paste, or duplicate, the original as many times as desired. Alternatively, just give a control the same name as an existing control of the same type in the same window; REALbasic asks if you're trying to start a control array, and assigns index numbers appropriately. Now you have cloned the control in the IDE. In the window's Code Editor, the control array is represented by a single Control entry and its event handlers.

Cloning in Code

Before you can add elements to a control array in code, the control array must exist. So, in the IDE, you will have had to give a control an Index number, which once again will almost certainly be 0. Now your code can append to the array dynamically: use New and the name of the control with no index.

For example, if you've created a button called PushButton1 and you've set its Index property to 0 in the IDE, then in code within that window's Code Editor, you can say this:

dim b as pushButton
b = new pushButton1

This will generate pushButton1(1), or whatever the next available index may be. The new element will be assigned the lowest available index value; again, that's why it's best to assign the original element the index 0. With New, we are here using an instance name, not a class name.[13] This looks like a violation of the rules of New's syntax, and that's exactly what it is. This exceptional syntax is permitted only in the case of a control array, so that you can clone.

When a clone is created dynamically, the newly generated control will copy the property values of the oldest element of the control array. This has important implications for what will happen at the moment of the clone's instantiation. For example, because the new clone will get its initial Left, Top, Width, and Height property values from the oldest clone, it will be located directly on top of that oldest clone at the moment it comes into existence.

Notice that New returns a value (the newly generated instance), and that we must do something with this value; in the example just given, we chose to assign it to a variable, so we were also forced to declare the variable. There is no trouble getting a reference to the newly generated clone, so this variable isn't really needed. But it's often quite convenient, because typically one will want to set the clone's properties the moment it is generated. For example, since the new clone will be located on top of the clone that it copies, you might want to move it, like this:

dim b as pushButton
b = new pushButton1
b.top = b.top + b.height + 5

You cannot clone a control except from code within the Code Editor of the window that contains it. The reason is that the New operator doesn't support the required syntax. Suppose that MyOtherWindow contains a button called PushButton1 whose Index is 0, and that from outside MyOtherWindow's Code Editor you try to say:

dim b as pushbutton
b = new myOtherWindow.pushbutton1 // error: Index expected for control array reference
b.top = b.top + b.height + 5

That doesn't work, because you're not allowed to refer to a control array without an index number from another window; you have to refer to a specific element of the array. So you try:

dim b as pushbutton
b = new myOtherWindow.pushbutton1(0) // this may not do what you think!
b.top = b.top + b.height + 5

That doesn't work either. No new button is created, because the New operator sees the name myOtherWindow and creates a new instance of that class. The rest of the second line merely obtains a reference to pushButton1(0) in that new window, so the third line ends up moving the old button in the new window, not (as intended) the new button in the old window. You cannot solve this problem by obtaining a reference to the window as a variable name, such as w, and then saying new w.pushbutton1; you'll get still another error ("Unknown identifier"), because w isn't a class name. (In case anyone cares, I regard this entire situation as a bug in the syntax of New.) The workaround, if you want to clone a control in window A from code in window B, is to give window A a method that clones the control, and have window B call it.

Being Careful with Instance References

The value of a variable or property has a datatype, and this datatype is either a scalar type or an object type. Scalar types are strings, numbers (integers, singles, doubles), colors, and booleans, all taken up formally in Chapter 5. Object types are classes, which is what this chapter is about. When a variable or property has an object datatype, the name of the datatype is the name of a class, and the variable or property can be used as a reference to an instance of that class.

So far, this chapter has explained how to generate an instance, and how to get a reference to that instance. Now it's time to talk about how to work with references to instances.

References to instances are tricky. Paradoxically, this is for the same reason they are so easy. REALbasic tries to shield you from the truth, which is that references to instances are pointers--the value of a reference to an instance is not the data (the instance), but an address (a numeric value stating where in memory the actual instance lives). The word "pointer" doesn't appear in this connection anywhere in the REALbasic documentation. The REALbasic language doesn't make you explicitly dereference a pointer in order to gain access to the properties of an instance; instead, such dereferencing is handled for you implicitly. In short, REALbasic tries to create the illusion that objects are just like scalars.

The purpose of this section is to shatter that illusion and trample its fragments in the dust. My experience is that beginners get into much less trouble when they understand what's really going on. If you remember that references to instances are pointers, and grasp the implications of that one simple fact, you'll manipulate such references successfully. The point is so important that it deserves a paragraph to itself, so here it is:

TIP:  A reference to an instance is merely a pointer. The instance itself lives elsewhere and has an independent existence.

Initialization Is Not Instantiation

Declaring a scalar variable or property creates a variable whose value is valid. When you say:

dim s as string

a string really does come into existence; s has a valid string value (the empty string). Similarly, declaring a variable or property as an integer, single, or double assigns that variable or property a valid integer, single, or double value (a form of 0); and declaring a variable or property as a boolean assigns it the valid boolean value false.

But objects don't work the same way. That's because an object name is merely a pointer; the instance itself has an independent existence (remember?). Declaring a variable or property as an object type uses the name of a class, but it doesn't instantiate that class. Getting an instance and assigning it to the variable or property is up to you. Until you do so, the variable or property has a value, which is nil. The value nil means merely: "I am supposed to point to an instance, but at the moment I don't." A variable or property whose value is nil is a nil pointer.

Only an instance can be sent a message; but nil isn't an instance. If you send nil a message, your application will terminate prematurely.[14] This means that you must be concerned with not using a nil pointer as the recipient of a message. The name of a variable, the moment that variable is declared as having an object datatype, is a nil pointer. When you Dim a variable to an object type, the variable is a nil pointer. If an array has an object datatype, then when that array is declared, all of its elements are nil pointers, and if the array is redimmed to create new elements, the new elements are nil pointers. If a class has a property with an object datatype, then whenever that class is instantiated, that property of the new instance is a nil pointer.

There are several ways to avoid sending a message to a nil pointer. One is simply to be careful. However, accidents will happen, so it is useful to have some programming tricks up your sleeve to make it less likely that you will forget what you're doing and accidentally send a message to a nil pointer.

Since you cannot avoid generating nil pointers, one rule of thumb is to keep those nil pointers as short-lived as possible; the moment you make one, you immediately replace its nil value with an instance, if you can. So, for example:

dim f as folderItem // f is a nil pointer
f = getFolderItem("myDisk:myFolder:myFile") // f is no longer a nil pointer, you hope

is better than:

dim f as folderItem
// ... two hundred lines of code, during which f is a nil pointer ...
f = getFolderItem("myDisk:myFolder:myFile")

Another device is to test the waters before you dive in, by comparing a value to nil before you use its name as a reference to an instance:

dim f as folderItem
// ... two hundred lines of code, during which f may or may not have been assigned an instance ...
if f <> nil then
    f.delete // f is an instance, so it's okay to send it a message
end

Testing the waters is especially important when your means for assigning an instance to a property or variable may or may not have worked. We've just seen an example. This line:

f = getFolderItem("myDisk:myFolder:myFile")

will have no effect if myDisk or myDisk:myFolder doesn't exist (as explained in Chapter 21). Testing f for nil afterward is your way to find this out.

Declaring an array of objects generates multiple nil pointers at once. For some reason, even beginners who understand how to declare a variable and initialize it with an instance value have a tendency to forget that the same thing must be done for every element of an array. Typically, you will iterate through the array, obtaining an instance to assign to each element.

So, for example, imagine that we wish to create an array of Date instances, and then to assign each instance a value by way of its TotalSeconds property (this will all be made clear in Chapter 5). This is not the way:

dim d(5) as date
d(1).totalseconds = 1764098908.0 // game over, thank you for playing

Before we can send a message to an element of the array, that element must refer to an instance. A For loop lets us create instances easily:

dim d(5) as date
dim i as integer
for i = 1 to 5
    d(i) = new date
next
d(1).totalseconds = 1764098908.0 // no problem

Assignment Is not Cloning

Scalar values are copied by assignment. When you say:

dim this, that as string
this = "Hello"
that = this
this = "Goodbye"

then that is "Hello" and this is "Goodbye". The value of this was copied to become the value of that; moreover, the two copies are independent of one another, so afterward, changing this does not affect that.

But when a value whose datatype is an object is assigned to a variable or property, what is copied is merely a pointer. That's because the value in question is merely a pointer; the instance itself has an independent existence (remember??). If the source of this value was a variable or property referring to an instance, the result is that two variables or properties now refer to the selfsame instance. So, suppose we say this:

dim w, w2 as myOtherWindow
w = new myOtherWindow
w2 = w
w2.top = 50
w.top = 100
msgbox str(w2.top)

The dialog box displays "100." But how can that be? We just said that w2's Top property should be 50, and we didn't change it; how did it get to be 100? The answer is that w and w2 are both just pointers, and after the assignment:

w2 = w

they point to the same thing--a third thing, an instance. Saying:

w.top = 100

sends a message to that instance. In other words, w and w2 don't have a Top property; they are just names. It's the instance that has a Top property, and in our code there is only one instance. Our code sets that instance's Top property, first to 50, then to 100.

On the other hand, after the assignment:

w2 = w

you should not imagine that w and w2 are "two names for the same thing." That is a misleading formulation, and can get you into trouble. There are languages, such as C++, that allow you to make two names for the same thing;[15] but REALbasic doesn't (except through the medium of ByRef parameters), and that's not what's happening here. Rather, w and w2 are names for two different things--two different pointers. It's just that those two pointers point to the same thing.

Figure 3-5 schematizes the process of instantiation and assignment: object names are pointers, and the instance has independent existence.

Figure 3-5. Object names are pointers

 

I have seen many REALbasic beginners, and indeed many REALbasic programmers of long standing, absolutely stunned by this behavior. They are used to the intuitive notion that assigning one scalar to another clones the scalar's value, and they expect the same thing to happen with objects. This misunderstanding results in some astonishing code. For example, I often see people do this:

dim o, o2 as myClass
o = new myClass
o2 = new myClass
o2.itsProperty = "test"
o = o2

This programmer is laboring under three misconceptions. First, he thinks that the last line is copying the value "test" from o2.itsProperty to o.itsProperty. Second, he thinks that after the last line he still has two instances of MyClass. Third, he thinks that he needs the second line to provide an instance to copy o2's properties into.

All three ideas are wrong. The last line causes o to stop being a reference to whatever it used to be a reference to, and to point to the very same instance that o2 points to. There is now only one MyClass instance. It's true that the second line generated an instance of MyClass, and caused o to point to it; but the last line caused o to point to the same instance as o2 instead, so the instance that o originally pointed to now has nothing pointing to it, becomes useless, and goes out of existence. That instance was utterly redundant, born only to die unused.

So, assignment is not cloning; it does not make a second instance with all the same property values as the first. How, then, do you clone an instance? The answer is, you do it the same way Superman gets into his trousers--one leg at a time. If you want the property values of one instance to be copied into the properties of another instance, you must copy them one by one:

dim o, o2 as myClass
o = new myClass
o2 = new myClass
o2.itsProperty = "test"
o2.itsOtherProperty = "test2"
// clone o2 into o
o.itsProperty = o2.itsProperty
o.itsOtherProperty = o2.itsOtherProperty

To be sure, if this is a class you have created yourself, and if this is something you're going to want to do frequently, you can abstract the cloning code into a method, and you very probably will. But that method must still copy the property values one at a time:

Sub clone(o as myClass)
    o.itsProperty = self.itsProperty
    o.itsOtherProperty = self.itsOtherProperty
End Sub

Once you are aware that assignment assigns a pointer, you can deliberately take advantage of this fact to achieve some very nice effects. Here are some examples.

Short names

If we have a button named PushButton1 in a window named MyOtherWindow, we can refer to it as myOtherWindow.pushButton1. But assignment gives us another way:

dim b as pushbutton
b = myOtherWindow.pushButton1

This makes subsequent references to our button shorter, and perhaps clearer. It isn't only a lot neater to say b.top than myOtherWindow.pushButton1.top, it can also make code faster. The reference myOtherWindow.pushButton1 gives instructions for reaching our button in stages: first go to MyOtherWindow, now go to its PushButton1. The reference b points right at our button in one step. This saves time, and in a loop where the instance is accessed many times, it can save a lot of time. My tests show that the savings is about 25% for this simple example; there are circumstances in which it can be considerably more.

Linked data structures

Many data structures you read about in computer textbooks depend upon pointers to make links. For instance, a stack is a data structure where a simple implementation is for each item in the stack to consist of a value and a pointer to the next item in the stack. Thus, a stack containing the values 1, 4, and 9 would be implemented as 1 plus a pointer to the next item, which is 4 plus a pointer to the next item, which is 9 plus a pointer to nowhere (a nil pointer).

Beginners sometimes wonder how to make this sort of structure in REALbasic; "Where are the pointers?" they ask. The answer is that they are built right in, thanks to assignment. So, to construct the stack in question, we'd start by defining a class (we'll learn how to do this in Chapter 4), which I'll call Stack; let the class have two properties, which I'll call ItsValue and ItsPointer. The datatype of ItsValue is integer. The datatype of ItsPointer is Stack, which really means (as we now know) that its value will be a pointer to a Stack--just the thing we're after. So now we can build the stack:

dim s, temp as stack
s = new stack
s.itsValue = 9
temp = s
s = new stack
s.itsValue = 4
s.itsPointer = temp
temp = s
s = new stack
s.itsValue = 1
s.itsPointer = temp

Now s is the first item of the stack, which consists of a chain of linked Stack instances. To show that this is true, we can loop our way down the stack, reporting the value of each item as we go:

temp = new stack
temp.itsPointer = s
while temp.itsPointer <> nil
    temp = temp.itsPointer // traverse one link
    msgbox str(temp.itsValue)
wend

The complete Stack class appears in the last section of Chapter 4.

Multiple references

If we have an instance and a reference to it, and we assign the reference to a second reference, we have two references to the same instance. This means we can do things to the instance by way of either reference. If this isn't what you intended, it's a bug in your program; but often it's exactly what you intended, and a great convenience.

To take a traditional sort of example, suppose we have various sorts of widgets (imaginary things that we sell), each of which has a price that fluctuates very often. And suppose we have orders from customers, which remain open until we are ready to fulfill them, and that we don't calculate the total on an order until we are ready to close it--that is, the price of each type of widget on an order is the price at the moment the order closes.

You might think that this means at order-closing time we must look up the current price of each widget in the order in some sort of database. But instead we can let pointers be our lookup. Let each widget type be represented by a reference to an instance of the Widget class; we'll call this the master list of widget types. Let an order be an object having as one of its properties an array of Widgets, denoting the widgets on that order. To describe the order, assign to each element of the array one of the instances from the master list. Now any time we change the price of one of the instances in the master list, then in all the open orders for that type of widget, the price changes too--because, for any particular type of widget, the widget reference in the master list and the references to that type of widget in all the different orders all point to the selfsame instance. So the prices of all the widgets in all the open orders are up to date all the time, automatically.

There are some pitfalls associated with multiple references; see "Destruction of Instances," later in this chapter.

Parameters Pass Pointers

When a scalar is passed as a parameter to a subroutine, its value is copied, so that the subroutine receives an independent copy--unless, of course, the subroutine is declared to accept the parameter ByRef:

dim s as string
s = "testing"
mungeMyString s

Assume that MungeMyString is not declared to accept its parameter ByRef. Then whatever MungeMyString may do, one thing is certain: after the call to MungeMyString, the value of s will not have changed. That's because s is passed by value, meaning that MungeMyString receives a copy of s's value.

Object references are passed by value too. So when an object reference is passed as a parameter to a subroutine, it is copied. But what's copied is a pointer! That's because an object reference's value is a pointer; the instance itself has an independent existence (remember???). So the subroutine ends up having access to the very same instance that the original object reference pointed to, just as with assignment. The subroutine can change the instance's property values, and these changes will be reflected back in the calling routine.

For example, suppose we have a subroutine that goes like this:

Sub moveMyWindow(theWindow as window)
    theWindow.top = 100
End Sub

Now we'll call the subroutine, as follows:

dim w as myOtherWindow
w = new myOtherWindow
w.top = 50
moveMyWindow w

Afterward, w.top is 100; MoveMyWindow does indeed move the window that is handed to it as a parameter.

Now, you might say: aha! Objects are passed by reference! But no: objects are normally passed by value, just like scalars; the difference is that with objects, the value passed is itself a reference. So, you may ask, what happens if you do pass an object by reference? I mean, since a subroutine that receives an object as a parameter by value can already change that object's property values, what further ability can it possibly gain by receiving that object by reference? The answer is that now you're passing a reference by reference, and so the subroutine now has the ability to change the reference--that is, to repoint the pointer.

To show why this is useful, here's an example involving Picture objects; these are not formally treated until Chapter 11, but the idea will be clear enough. A Picture object is created with a call to the built-in NewPicture function, specifying the height, width, and depth of the picture; then it is possible to draw into the picture. Let's say I have a variable declared as Picture, and I want it to contain a drawing. But I don't want to do the drawing myself; I want to hand the variable to a subroutine and let the subroutine do the drawing, so that I end up with a reference to the completed picture.

Name the subroutine DrawMyPicture, and let it take a single parameter which is a Picture. Then I can say:

dim p as picture
p = newpicture(100,100,16)
drawMyPicture p
// ... do something with p ...

After the third line, p points to the Picture instance containing the drawing, and I can use it as I please.

Now let's suppose this is not quite what I wanted DrawMyPicture to do. You notice that in the second line I created the Picture object myself. But let's say I don't know what the height and width and depth of the picture should be, and I don't want to know. Instead, that should be part of DrawMyPicture's job; it should create the picture and draw into it. So, I want to be able to say:

dim p as picture
drawMyPicture p
// ... do something with p ...

We can imagine that DrawMyPicture must look something like this:

Sub drawMyPicture(whatPicture as picture)
    dim theWidth, theHeight, theDepth as integer
    // ... figure out size of picture ...
    whatPicture = newpicture(theWidth, theHeight, theDepth) // error
    // ... draw into the picture ...
End Sub

There's just one problem: we get an error ("Parameters can't be modified"). The solution is to declare DrawPicture such that whatPicture is ByRef:

Sub drawMyPicture(byRef whatPicture as picture)
    dim theWidth, theHeight, theDepth as integer
    // ... figure out size of picture ...
    whatPicture = newpicture(theWidth, theHeight, theDepth)
    // ... draw into the picture ...
End Sub

This works, and shows how passing an object reference by reference can be useful.

But now let's turn up the heat still further. Instead of declaring p as a local variable, let's suppose p is a property. In this scenario, our main routine selects a Picture property and hands it to DrawMyPicture to create the actual picture and draw into it. So now our main routine goes like this:

drawMyPicture p // p is a property: error
// ... do something with p ...

Now there's a new problem; we get another error ("Reference parameters can only be local variables"). We have run smack into one of REALbasic's strangest limitations: you can't pass a property ByRef. What are we to do? If we declare DrawMyPicture so that its parameter is not ByRef, we can pass a property but we can't set it with NewPicture. If we declare DrawMyPicture so that its parameter is ByRef, we can set it with NewPicture but we can't pass a property.

The solution is to rearchitect the property so that it is itself a pointer--in other words, we're going to pass a pointer to a pointer. This sounds obscure, but it's quite simple. We create a class, which I'll name PicturePtr, consisting of a single Picture property named ItsPicture. Let the property p be a PicturePtr, not a mere Picture. Now here is DrawMyPicture:

Sub drawMyPicture(whatPicture as picturePtr)
    dim theWidth, theHeight, theDepth as integer
    // ... figure out size of picture ...
    whatPicture.itsPicture = newpicture(theWidth, theHeight, theDepth)
    // ... draw into the picture ...
End Sub

The parameter no longer needs to be ByRef. We pass (by value) an object, which is a PicturePtr. This means that DrawMyPicture receives a pointer to the PicturePtr instance, and therefore has complete access to all that instance's properties. A PicturePtr has a Picture property. Therefore DrawMyPicture has complete access to the Picture, and can assign to it.

This technique of devising a class that itself functions as a pointer is of wide utility in REALbasic, and will be taken up again in Chapter 4.

Comparison Tests Identity

Object comparison differs from scalar comparison. For one thing, concepts like "greater than" and "less than" are largely meaningless when applied to objects; REALbasic won't complain, but the results of the comparison won't be of any use to you. Therefore, the main comparison you're likely to use on an object reference is equality comparison with another object reference, or with nil. The purpose of comparison with nil has already been explained: it's to determine whether a reference is a nil pointer. So it remains only to talk about equality comparison between instances.

Equality comparison between two instances asks whether they are the selfsame instance, not whether they have the same "value" (whatever that would seem to mean). For example, beginners are often misled into writing code of this sort:

dim d1, d2 as date
d1 = new date
d2 = new date
// ... set d1 to a particular date ...
// ... set d2 to a particular date ...
if d1 = d2 then // this will never succeed!

The trouble here is that the programmer is subconsciously equivocating on the notion of a "date." A Date object is a way of expressing a date. The programmer wants to know whether both Date objects are expressing the same date (for example, are they both August 10, 1954?). But that's not what equality between object references tests; it tests only whether the references are to the selfsame instance, and we know they are not, because we saw them created as two separate instances. (How to test whether two Date objects express the same date is irrelevant here, and is explained in Chapter 5.)

Again, consider the FolderItem desktopFolder.child("test"), which denotes a file called test on the desktop. The condition in this line:

if desktopFolder.child("test") = desktopFolder.child("test") then

is false, because two separate instances are formed by the two separate function calls on the two sides of the comparison. (It's true that those two instances provide access to the same file on disk, but that's irrelevant.)

On the other hand, after executing the following:

dim f, g as folderItem
f = desktopFolder.child("test")
g = f

the variables f and g point to identically the same instance, and so the boolean expression in this line:

if f = g then

is true.

Destruction of Instances

We know how instances are born; but how do they die? The short answer is that, in general, you're not supposed to worry about this. REALbasic does its best to shield you from problems of instance destruction. Those problems are intimately related to memory management; and one of REALbasic's great strengths is that it manages memory for you.

The core of REALbasic's memory management system is a principle called garbage collection. This states that REALbasic will see to it that an instance will go out of existence all by itself when appropriate, and the memory that it used to occupy will be cleared. In particular, REALbasic uses a style of garbage collection called reference counting; this states that an instance is destroyed as soon as there is no way at all for code to refer it. Internally, the implementation is that every instance is accompanied by a property, its reference count, invisible to the programmer, which is incremented every time a reference is assigned to point to that instance, and decremented every time such a reference ceases to point to the instance or ceases to exist. You can manually cause a reference to cease to point to an instance, by setting its value to nil; otherwise, a variable goes out of existence automatically when its subroutine terminates execution, and a property goes out of existence automatically when the instance that owns it goes out of existence. An instance is destroyed when its reference count drops to zero. (Actually, the programmer can get access to an instance's reference count, through the built-in Runtime object; this is discussed in Chapter 8.)

For example, if a subroutine instantiates a FolderItem, assigning it to a variable declared within that subroutine and not assigning it to any other name, then once the subroutine has finished executing, the FolderItem instance is no longer pointed to by any name at all. Therefore, the instance is useless and it will be destroyed. On the other hand, if a subroutine instantiates a FolderItem, assigning it to a property, and if the instance containing the property lives on after the subroutine has finished executing, the FolderItem instance is still pointed to by the property name, and will persist as long as the instance containing the property persists.[16]

Some instances seem to work differently. Chief among these are window instances. Windows have a life of their own, as it were, because they are fundamental features of the interface. If a window vanished just because your code stopped talking about it, the user would see a lot of unpleasant action on the screen! So, it seems that windows persist in defiance of the garbage-collection rules. However, that isn't really true, because even if your code maintains no name by which to refer to a window, there are ways to retrieve a reference to it--for example, by calling the Window function.

On the other hand, windows are certainly special in this sense: since they don't die automatically, your code has to have a way to kill them. Therefore, windows accept a Close message as a way of telling an instance to die. This has a secondary effect: all the window's controls are also destroyed. If an instance is a control in a window class, it is brought into existence automatically when the window is instantiated, and is destroyed automatically when the window is destroyed. (And recall that any class that appears in the Project Window or Tools Window, even if it is not a subclass of the built-in Control class, can be instantiated as a control contained by a window, by dragging it into a Window Editor.) Similarly, consider a property of a window class, whose datatype is an object class: after the window is instantiated, the property does not automatically point to any instance (it's up to your code to cause it to do so); but once it does, if there are no other persistent references to the instance pointed to by that property, then when the window is destroyed, the instance pointed to by that property is destroyed. Thus, windows are an important device for easy management of instance lifetimes.

On the whole, you're just supposed to have faith in REALbasic's memory management and not be particularly conscious of it. There are, however, two exceptional situations. One is when you want to prevent yourself from running short of memory by releasing an object's memory right now. This comes up particularly when a reference to an instance is a property, and you know that the instance containing this property isn't going to go out of existence any time soon; you can sever the property from the instance that it points to by setting it to nil, and if this is the last reference to that instance, the instance is immediately destroyed.

A common mistake is forgetting that a reference is not the only reference to an instance. I've been working with REALbasic for years, and I still sometimes get this wrong! For example, suppose C is a property declared as MyClass, which has a Picture property P, and that C already points to an actual instance of MyClass. Here, for some reason, we choose to work with c.p through a secondary pointer:

dim pp as picture
c.p = newpicture(100,100,16) // c is a property and has been instantiated
pp = c.p
// ... do things with pp ...
pp = nil // free up the memory--not!
// ... more code ...

Setting pp to nil doesn't free up the memory occupied by the Picture, because c.p is still pointing to it. A better approach might have been for the secondary pointer to point at C:

dim cc as myClass
cc = c
cc.p = newpicture(100,100,16)
// ... do things with cc.p  ...
cc.p = nil
// ... more code ...

The other situation where you need to take a hand in REALbasic's memory management is when you've created a circular reference. Suppose we have two classes, A and B, and that A has a property ItsB, which is of datatype B, and that B has a property ItsA, which is of datatype A. Now we create instances of A and B and point their properties at one another:

dim a as A, b as B
a = new A
b = new B
a.itsB = B
b.itsA = A

After this subroutine finishes executing, neither the A instance nor the B instance can be referred to, because our only references to them, the variables a and b, have ceased to exist; but the A instance and the B instance both persist anyway. The reason is that there is still a reference to the B instance (the A instance's property), and there is still a reference to the A instance (the B instance's property). To release the memory, you needed to set one of those properties to nil before the subroutine finished executing. The situation looks artificial when presented in skeletal form, but in fact it is quite prone to arise in object-oriented programming and when you're using data structures that form closed loops of pointers (such as doubly linked lists or circular queues).


1. By making it part of a module. Modules are discussed in Chapter 4.

2. I simplify somewhat. In Chapter 4 we see ways to modify this behavior.

3. Some implementations of object classes permit class properties, whose value is in common for every object of that class. Similarly, some implementations of object classes permit class methods, where a message can be sent to a class without directing it at any particular instance. REALbasic does not have class properties or class methods, but the lack is not a very serious one, because REALbasic allows global properties, global methods, and global instances, as explained in Chapter 4.

4. My translation.

5. What categories of member appear in a Code Editor depends upon what's being edited; a window's Code Editor shows the widest range of categories. One category, Constants, is absent; but this category, which refers to a global version of the local constants described in Chapter 2, appears only in a module, and a module isn't a class. Modules and global constants are discussed in Chapter 4.

6. Actually, when defining both a class and its subclass, there is a way for the programmer to add an event handler, and to call it (though in an extremely limited context). This is called a New Event, and is discussed in Chapter 4.

7. The syntax of New must be modified if the class being instantiated has a constructor; this is explained in Chapter 4.

8. For IsA, which reports whether an instance is of a given class, see Chapter 4.

9. For IsA, see Chapter 4. The equality test with instance names is discussed later in this chapter.

10. But MenuItem clones can be destroyed. See Chapter 6.

11. The workaround is to construct normal arrays and use their elements to point to the members of the control array; but at that point the control array itself becomes meaningless.

12. I owe this tip to Guillaume Grenier.

13. In the Dim statement, we might instead have used the instance name, pushButton1.

14. Actually, it will raise an exception (a NilObjectException); you can catch this exception to prevent premature termination, as explained in Chapter 8.

15. Confusingly, C++ calls such alternative names "references."

16. The whole thing is rather like the haunting short story, "Do You Love Me?" by Peter Carey. The world has been taken over by the Cartographers, who with their elaborate lists of everything have essentially usurped God's task of upholding all things by the word of his power. As the story ends, whatever and whoever is not loved by someone, is ceasing to exist. In effect, it's a world with garbage collection! See Peter Carey, The Fat Man in History and Other Stories (Faber and Faber, 1980).

Back to: Sample Chapter Index

Back to: REALbasic: The Definitive Guide, 2nd Edition


oreilly.com Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies | Privacy Policy

© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com