TweetFollow Us on Twitter

OOP Architectures 2
Volume Number:7
Issue Number:3
Column Tag:OOP Architectures

MacApp and THINK Class Library

By Larry Rosenstein, Apple Computer, Inc.; Joseph S. Terry, Jr., Ajalon Corporation

Part Two

Object-oriented Design (OOD) ...

“Design is the most difficult and important task of Object Programming (OP) and very few professional software engineers do it well.”

That sentence began the first of this two-part(read long) article and it bears repeating. Goods tools in and of themselves do not produce reliable, well designed products, well trained software engineers do.

In this second part we will explore the execution of commands which are mostly menu choices or button clicks, look at general flow of control from one class to another, clear up some confusion about exactly what does go on under low memory conditions (and doesn’t), take a look at printing, using background tasks or chores, error handling, and a summary of the things to remember when programming with any class library.

The last seven sections are the ones we’re covering in this second part. The first five sections were covered in the January 1991 MacTutor.

1 - BIT O’ HISTORY

2 - ARCHITECTURAL OVERVIEW

3 - HOW THINGS WORK TOGETHER: An Example Message Trail

4 - STORING INFORMATION

A) APPLICATION->DOCUMENT->FILE RELATIONSHIPS

B) IN MEMORY DATA STRUCTURES

5 - DISPLAYING INFORMATION

A) APPLICATION->WINDOW->VIEW RELATIONSHIPS

6 - EXECUTING COMMANDS

A) CONTROLLING THE CHAIN OF COMMAND

B) TO UNDO OR NOT TO UNDO

7 - FLOW OF CONTROL

8 - LOW MEMORY CONDITIONS

9 - PRINTING

10 - BACKGROUND TASKS

11 - ERROR HANDLING

12 - SUMMARY

MACAPP

Larry Rosenstein

Apple Computer, Inc.

EXECUTING COMMANDS

A large part of an application’s code is devoted to processing commands. In MacApp, commands are represented by the TCommand class. TCommand is not only used to factor out commands from the rest of the application, but also to encapsulate the information necessary to undo the command (if the need arises).

There are 4 methods of TCommand related to performing actions: DoIt, UndoIt, RedoIt, and Commit. When the command is first performed, MacApp calls the DoIt method. DoIt is responsible for saving information in case the command is undone and performing the command.

UndoIt is called if the user selects the Undo menu item. RedoIt is called if the user selects Undo again. DoIt and RedoIt are similar and often share code. If the user selects Undo a third time, then MacApp calls UndoIt again, and so on.

The purpose of the Commit method isn’t as obvious. It is called when the command can no longer be undone (e.g., the next command is about to be performed) and the command hasn’t been undone. This gives the command object one last chance to affect the document before it is freed.

Commit is used in cases where the command isn’t easily reversible. In a drawing program, moving shapes is easily reversible. The command object simply remembers the offset and moves the shapes by the same amount in the opposite direction. A command that changes the fill color, however, isn’t easily reversible. Reversing the command requires that the program remember the old color of each affected shape. This could take almost as much memory as the entire document.

In this case, the command could be implemented using a filter. When the user changes the fill color of one or more shapes, the color stored in the shape objects isn’t changed immediately. Instead the program remembers which shapes were affected, and what color they were changed to. When drawing the affected shapes, the program uses the color in the filter. To the user the shapes have changed color.

Undoing the command is now easy, because we simply remove the filter. The shape objects are then drawn with their original color. Eventually, however, the shapes’ colors must be permanently changed; this would be done in the Commit method.

Now that we know how command objects work, where do they come from? A MacApp application creates command objects in response to user actions. The 3 main cases are menu selections, keystrokes, and mouse clicks.

Menu selections and keystrokes are handled in a similar manner. MacApp takes case of handling the menu or extracting the character, and calls the DoMenuCommand or DoKeyCommand method. The target event handler gets the first chance at handling the event; control passes along the target chain until it reaches the object responsible for handling the event. At that point, the event handler object creates the appropriate command object and returns it to MacApp.

In the case of a mouse click, MacApp locates the view in the hierarchy on which the user clicked, and calls the DoMouseCommand method of that view. DoMouseCommand creates a command object and again returns it to MacApp. This command object tracks the mouse, and (usually) performs the command when the user releases the mouse button.

There are 3 method of TCommand involved with mouse tracking:

• TrackMouse, which receives the current position of the mouse and processes it,

• TrackFeedback, which draws and erases the temporary feedback while tracking,

• TrackConstrain, which modifies the mouse position before the other methods see it, in order to implement constraints (e.g., constraining a shape to be a perfect square).

MacApp calls these methods as the user moves the mouse. The tracker doesn’t have to worry about automatic scrolling (if the user moves outside the window). TrackMouse returns a command object, which becomes the new tracker. (In most cases, the command object returns itself, but it is possible for one tracker to “hand off” to another.)

When the user releases the mouse, TrackMouse returns the command object that executes the mouse command. For example, if the user is sketching a new shape, this command object is responsible for adding the shape to the document. (Normally, the same object performs the command as tracks the mouse.)

Not all menu commands or mouse clicks result in significant changes to the document. In those cases, MacApp provides a global command object (gNoChanges) that the application can return. It is also possible to create a custom command object and mark it as one that doesn’t change the document. (One advantage of the latter approach is that the command will be performed from the main event loop, rather than from a point several procedure calls deep. This may be preferable from a memory management standpoint.)

FLOW OF CONTROL

LOW MEMORY CONDITIONS

Memory management is one of the more difficult aspects of Macintosh programming. Applications are only given a limited amount of memory to work with, so it is always possible to run out. If this happens while trying to load a code segment, for example, the result is the dreaded System Error alert.

MacApp cannot handle all the details of memory management. It does provide a framework that you can use. One goal of the memory management framework is to prevent a System Error because a code segment or other resource can’t be loaded. Another goal is to ensure that the user can always save the document and quit.

MacApp’s memory management framework is based on reserving RAM space so that it can be used at critical times. MacApp maintains 2 reserves, one for memory associated with the document (“permanent” memory) and one for “temporary” memory. MacApp maintains a global variable to indicate whether memory being allocated is temporary or permanent.

There is no distinction between permanent and temporary memory as far as the Macintosh Memory Manager goes. The difference is only significant in what happens when memory is low. When memory is low, MacApp will release the appropriate (permanent or temporary) reserve in order to allow the request to succeed. This is done by installing a procedure that the Memory Manager calls when an allocation request can’t be immediately satisfied.

Requests for temporary memory must always succeed, since the alternative is likely to be a System Error. Permanent requests are allowed to fail; MacApp assumes that the programmer will check the result of these allocations and fail gracefully if one fails.

Most of MacApp’s memory management framework involves managing the reserves. It is important to have sufficient reserve available, so that the application doesn’t crash, but it is also important not to monopolize that memory unnecessarily, so that it can be used.

The size of the reserves is normally set based on resources (although you can set them programmatically as well). The permanent reserve is generally small, since it isn’t intended to satisfy every request. The temporary reserve, on the other hand, must be large enough to cover the maximum amount of temporary memory in use at any one time.

In practice, this number ends up being the maximum size of the code segments locked down at any one time. (With proper segmentation, this number is only a fraction of the size of all the code segments.) Sometimes you have to include memory used by defprocs, packages, print drivers, etc.

The MacApp debugger provides utilities for determining the maximum code usage. You can have the debugger break when each segment is loaded, or when a new maximum usage is reached. Then you can list the names of the loaded code segments and enter then into a ‘seg!’ resource in your application.

When the application starts up, MacApp adds up the sizes of all the segments listed in all the ‘seg!’ resources and adds that value to the temporary reserve. In addition, MacApp examines all the resources of type ‘mem!’. Each of these resources contains values that are added to the temporary reserve, the permanent reserve, and the stack size.

Once you have specified the sizes of the reserves, MacApp takes care of ensuring that the reserves are available. For the permanent reserve, MacApp allocates a handle of the required size. If a permanent allocation cannot be satisfied, then MacApp releases the entire permanent reserve.

You can test to see if the permanent reserve has been released by calling the function MemSpaceIsLow. MacApp does this during its idle processing, and calls the method TApplication.SpaceIsLow is the reserve is gone. By default, this method displays an alert. In addition, MacApp disables certain commands (New , Open , etc.) if memory space is low; you are responsible for doing a similar thing for the commands that you handle.

Managing the temporary reserve is more complicated. MacApp could allocate a large handle the size of the reserve. Unfortunately, this would often waste memory. For example, suppose your application needs a temporary reserve of 100K to load code segments A, B, and C. If none of the segments is in memory, then MacApp would have to allocate a 100K handle to make up the reserve. If segment A is already in memory, however, then the size of the handle only needs to be the total of the sizes of B and C.

In its implementation, MacApp total up the sizes of code segments, packages, and defprocs that are already in memory, and subtracts this sum from the desired temporary reserve. If the result is greater than 0, then MacApp allocates a handle to make up the difference.

MacApp doesn’t keep track of every resource as it comes and goes. If you think about it a minute, the integrity of the temporary reserve is only critical when a permanent request is made. That’s because you don’t want to satisfy the permanent request at the expense of reducing the temporary reserve. So MacApp doesn’t have to check the temporary reserve until the temporary/permanent flag is set to permanent.

This memory management framework is a good example of how MacApp can embody accumulated Macintosh programming knowledge, and make it available to other programmers. The basic approach of maintain reserves and managing them was first implemented in Apple’s Macintosh Basic, and re-implemented in the first version of MacApp.

PRINTING

Printing is another feature that can be difficult to implement on the Macintosh. It requires that you interact with the Printing Manager, and that you follow a definite series of steps. This makes it ideal for an object-oriented approach such as MacApp’s.

In a MacApp program, you rarely have to write any code to implement printing in your application. Once you have defined a view object that can draw on the screen, MacApp uses the same view object to “draw” on the printer. MacApp takes care of the various printer dialogs for you.

All the printing code is encapsulated in the TStdPrintHandler class. When creating your view object, you can make it printable by associating it with an instance of TStdPrintHandler.

Although most application don’t need to modify MacApp’s printing behavior, TStdPrintHandler provides many opportunities to do so. A simple change is to set the page margins. This is done with a call to TStdPrintHandler.InstallMargins.

Another common change is to modify the way MacApp breaks the view into pages. By default, MacApp computes the part of the view that can be displayed on each page, based on the desired margins and the printer’s characteristics. In then divides the view into chunks of that size. If you are implementing a text processor or spreadsheet, however, you would want to break the pages between lines of text or spreadsheet cells.

Before finalizing a page break, MacApp calls the view method DoBreakFollowing. You can override this method and move the page break back to a convenient boundary. You can also implement manual page breaks by overriding the same method. It is also possible to change the way in which page breaks are drawn on screen by overriding TView.DoDrawPageBreak.

A final printing variation is to add elements to the page that aren’t drawn on screen. By default, MacApp reproduces the entire view on the printed page. If you wanted to draw page numbers or headers, however, you could override the method TStdPrintHandler.AdornPage. This method is called once for each printed page, after the main part of the page has been drawn.

BACKGROUND TASKS

MacApp provides a simple framework for performing tasks in the background. This takes the form of the TEvtHandler.DoIdle method, which MacApp calls repeatedly when there are no user events pending. The TApplication, TDocument, and TView classes are all subclasses of TEvtHandler, so any instance of those classes can receive idle time. Additionally, you can define a special subclass of TEvtHandler to do other idle processing.

Instances of TEvtHandler can be linked into chains. One is the target chain, which normally starts with a view object, and continues up the view hierarchy to the window, document, and application. As mentioned before, this chain also handles keystrokes and menu commands. The other chain is known as the co-handler chain. This is a separate chain into which you can install your own TEvtHandler object.

Not every instance of TEvtHandler is given idle time (by calling its DoIdle method). Each instance of TEvtHandler contains the field fIdleFreq, which indicates the minimum interval between calls to DoIdle. If that field is the constant kMaxIdleTime, then MacApp does not give the object any idle time.

Actually, MacApp’s idle processing is a bit more elaborate. The DoIdle method has 1 parameter, which is the idle phase. MacApp defines 3 phases: idleBegin, idleContinue, and idleEnd.

When MacApp starts its idle processing, it calls DoIdle with the idleBegin phase. It then calls DoIdle with the idleContinue phase, subject to the value of the fIdleFreq field. When idle processing completes (i.e., the next user event is available) MacApp calls DoIdle with the idleEnd phase.

In order to use this idle mechanism, you need to implement your background task so that it can do a small amount of processing each time DoIdle is called. Since the idle processing is done by an object, you can maintain state between calls in the state of the object.

ERROR HANDLING

Error handling is a very important application feature. In designing the error handling framework for MacApp we wanted to provide an easy way for programmers to catch and recover from errors, and a clear way of reporting errors to the user.

To help catch and recover from errors, we implemented an exception handling mechanism in MacApp. MacApp maintains a stack of exception handling routines. You push a handle on the stack with a call to CatchFailures, passing a handler procedure, and a FailInfo record.

The record is initialized by MacApp with information necessary to call the handler (e.g., current stack pointer, a link to the next handler record). The handler procedure is declared with two parameters: a standard Macintosh error code, and a 4-byte message. The message is used to describe what operation failed, and the error code gives the reason for the failure. Normally the handler is a nested procedure in Pascal so that it has access to the local variables of the enclosing procedure.

There are two ways to pop a handler off the stack. The first is by calling Success. Success just pops the handler off the stack. You must do this before exiting the procedure that established the handler.

The second is by calling Failure. Failure can be called by any routine, not just from within the routine that set up the handler. When you call Failure, MacApp pops the top handler off the stack, restores the registers and other state, and calls the handler with the error code and message passed to Failure. (Note that the process of restoring the registers restores the stack pointer to its original position.)

A failure handler normally does some clean up (e.g., free allocated memory) and returns. MacApp then propagates the failure by calling the next handler on the stack. Eventually, control reaches a handler in the main event loop, which was established by MacApp. This handler displays an alert reporting the error, and the branches to continue handling events.

In order to make things easier for you, MacApp provides a few utility routines. For example, the procedure FailOSErr takes an error code and calls Failure is it is not noErr (0). This makes it easy to write code such as: FailOSErr(FSRead(...)); Similarly, there are utility procedures that check for a NIL return from the Memory Manager, a missing resource, etc.

Catching errors is still your responsibility; MacApp doesn’t try to second-guess what you are trying to do. But if you do use the MacApp failure handling mechanism, then MacApp will automatically put up the appropriate error alerts for you.

MacApp error messages have the general form:

Could not <operation> because <reason>.  <Recovery>

The alert has 3 blanks that MacApp fills in based on the error and message values passed through the failure handlers. In the simplest case, the error code is looked up in 2 tables to produce an explanation of the error and a way of recovering from it. The message code is looked up in another table to produce a description of the operation that failed.

An example of a filled-in alert might be:

Could not save “test doc” because the file is locked.  Use the “Get Info” 
command in the Finder to unlock the file.

The alert occurs if the user tries to save to a locked file. MacApp automatically generates this alert (including substituting the name of the document), provided you test for File Manager errors in your saving code.

If you look at all the possible Macintosh error codes, you will see that there are only a handful of errors that can be meaningfully reported to users. Also, many error codes can be described with the same message.

MacApp uses an ‘errs’ resource to map between an error code and an index into a list of strings. The resource consists of a list of entries, each of which contains an error code range, and the appropriate string index. (Special entries in the errs resource indicate the resource ID of the string list.)

There are a couple of variations on the basic error reporting mechanism. For example, if the failure occurs while processing a command, MacApp will use the command number of the command to retrieve the command name from the menu, and display an alert such as:

Could not complete the “Copy” command because there is not enough memory.

The failure handling mechanism also comes in handy for other things besides errors. For example, when the user closes a document that has been changed, MacApp puts up the standard “Save Changes...” alert. If the user clicks Cancel, the program should abort closing the document.

MacApp handles this case by signaling a failure if the user clicks Cancel. In this case, the error code passed is noErr. In the main event loop, this error code is handled specially, and MacApp does not display an alert. The end result is that the action is cleanly canceled, without the intermediate procedures having to handle this case explicitly.

FINAL COMMENTS

At the start of this series, Joe Terry asked a couple of questions, that I can now answer.

• How does MacApp make programming the Macintosh easier?

There are a number of ways that MacApp helps Macintosh programmers. First, it handles the common features that every application must have (menus, windows, scrolling, printing, etc.). This means that there is less code for you to write, which leaves more time for you to write application-specific code (e.g., new features).

Consequently, there is no need for you to understand all of Inside Macintosh, before you can start writing your application. Even though MacApp itself is large, you don’t need to understand very much of it before you can get something up and running. Spending a month learning about MacApp is a much better time investment than spending a month learning the Macintosh Toolbox. At the end of that time, your MacApp application will support scrolling, printing, Undo, etc.

MacApp is also beneficial even after you have learned its basics. MacApp provides a well-tested framework into which you add your application-specific code. This makes your application much easier to extend later. You will also end up building your own class libraries, which will make the second and later MacApp programs that much easier to produce.

Finally, MacApp encapsulates many programmer-years of Macintosh programming experience. Apple has been working on MacApp since 1985, and programmers who use MacApp gain the experience and testing that went into it.

• What are some of the design decisions that are easily reversed and some that are not so easily reversed?

Most of MacApp’s design decisions are easily reversed because MacApp is provided in source form and is linked with each application. Changes to MacApp don’t affect existing applications, unless they are recompiled. Of course, existing applications don’t get the benefit of any new features or bug fixes. (This is different from the Macintosh ROM, which is shared by all applications.)

In the past, developers using MacApp have favored improving it even at the expense of changing the programming interface. For example, MacApp 2.0 completely changed the view model, compared to MacApp 1.0. Developers had to spend some time upgrading from 1.x to 2.0, but they were not forced to do so until it was convenient.

MacApp 3.0 is changing the way documents and disk files are handled to make this area more general. Previously, TDocument combined the storage for a document with its I/O aspects; these functions are being separated.

MacApp 3.0 will also provide support for System 7 features (AppleEvents, Edition Manager, etc.). Although I haven’t looked at these MacApp changes, I’m certain that developing applications that support System 7 will be much easier if you’re using MacApp.

• What kind of applications is MacApp “best” for?

MacApp is intended for writing commercial quality applications. It isn’t intended for DAs or standalone code resources. Those kinds of programs could take advantage of an object-oriented framework, but such a framework would be different from an application framework (i.e. MacApp).

So far, MacApp has proven to be suitable for a variety of applications. There are MacApp applications already shipping that deal with 32-bit color graphics, text processing, sound & multimedia, and programming. MacApp was oriented more towards full applications, as opposed to small utilities. You can still write small utilities, but you won’t benefit as much from MacApp’s standard features.

TCL

Joseph S. Terry, Jr.

Ajalon Corporation

EXECUTING COMMANDS

CONTROLLING THE CHAIN OF COMMAND TO UNDO OR NOT TO UNDO

In TCL the concept of a “chain of command” is really a way of reducing the message passing overhead you would experience in a free form approach to message resolution. Rather than ask every object if a message applies to them, TCL has a specific line of objects that can respond to “direct commands”. At the head of the line is an object called the gopher. The gopher is actually a global variable called “gGopher” with a reference to the object that should be first in line to receive direct commands. The programmer is responsible for setting the global variable gGopher when you want a particular object to be at the head of the line such as a TextEdit field in a database application. When the user hits tab then you would make the next edit field in line the gopher.

Figure 1. Be a gGopher

Also you should note that the supervisor of the Pane that is the current gopher is likely to be the Document that is responsible for the window, rather than its immediate superpane, that the Pane in question is contained in.

Objects in the chain of command are decedents of the CBureaucrat class. This provides a common command handling behavior that all objects in line understand. Every CBureaucrat has a supervisor. If an object cannot handle or doesn’t understand a message then that message is passed on to it’s supervisor. CBureaucrat actually has an instance variable named “itsSupervisor”. This variable is initialized by the default initialization routine, as long as you provide an object as the supervisor.

To implement Undo-able actions you’ll want to use the class CTask. It is designed specifically to for this purpose. Undo-ing on the Macintosh is a central difference in the user interface approach from previous icon/pointer driven environments. But many people quickly realized that you only have one shot, that undo although wonderful was one level deep. If you did anything else after your mistake you could no longer undo the “bad” thing. The explanation of CTask in the TCL manual is short but complete. Remember to a subclass of CTask and store your own instance variables that will contain enough information to undo the action.

If its text changes that your undo-ing undoes then save the whole text. Trying to figure out what piece of localized text you’re undo-ing changes to, at undo time, is rather a ridiculous waste of brain power, use memory instead.

The really exciting news is that there is no excuse for a program written in TCL to not have much more sophisticated undo/redo scenarios. Here is where the class library begins to synergize solutions for us. With CTask and CList, I can build an unlimited undo for a simple action such as the font example in the manual in a page of code. Yes, unlimited undo/redo! I say a page when it could probably be done in a half a page if I didn’t include comments.

And the whole thing would make more sense if I had multiple inheritance at my disposal. That’s another article. What I would do is override the CDocument class Notify() method. What it does now is maintain the lastTask in an instance variable named “lastTask”. And on entry if there is a lastTask already then that is simply disposed of. I would continue to add any undo/redo task onto the current document task list in entry order and allow the user to set a limit on how many of these are kept around. As a new task entered the queue the oldest would be disposed of if the queue were filled.

If you did an example of undo-able programming try CMouseTask. Its not very clear from the manual so print out the source code and read it carefully. You’ll see that there are no instance variables defined in the “.h” portion of the source and the “.c” source includes only empty methods. This is the proverbial exercise for the reader. Look at the code for a mouseTask in Gregory H. Dow’s (GHD) CPaintTask.c in the project Art Class. This is very nicely done. It’s not optimal in speed of execution, but remember that in programming the “meta machine” of a class library it is more important to make “efficient” use of the the class library.

FLOW OF CONTROL

In MacApp there is a concept called the target. The target is the object instance upon which some command will act.1 In, TCL there is the gGopher. Further, there are three conceptual “target chains” which are the paths along which message with travel in search of a handler. There is the command chain, the click chain, and the cohandler chain. These are roughly equivalent to the “chain of command” and “visual hierarchy” concepts in TCL.

Note the similarity between handling a mouse click and menu command between MacApp and TCL. Since a mouse click involves a position in the window, it is handled by going down the view hierarchy in MacApp. Similarly, in TCL a mouse click in a particular position in a window goes first down the visual hierarchy DeskTop->Window->SubView->SubView->TargetSubView. A menu selection has no positional information and is handled by starting at the target/gopher and working upward from SubView->SubView->Document->Application. Most menu commands are normally handled in the Application Object. The only “classical” exceptions are the Apple, File, Font, Size and Edit menus which have some default behavior built-in.

Architecturally speaking, menus are a very, very old user interface concept and I expect some young bucks to come up with new more powerful and more ... human oriented, ways of making choices. Menus have proven useful, but as applications become more complex and the range of choices become larger, in my opinion, user interface designers are failing to create intuitive menu selection arrangements and it may not be their lack of talent or creativity, but a fundamental limitation in the conceptual framework or “design school” they are working from. For instance in the January 1991 MacTutor there is an article about Pie Menus by Boyd and Andrea Hays of Boulder, CO. This is the kind of article we all need to see more. This piece is based on an article called “An Empirical Comparison of Pie vs. Linear Menus”, by J. Callahan, D. Hopkins, M. Weiser, and B. Shneiderman[sic], in the ACM’s SIGCHI 1988 Conference Proceedings. The general conclusion of the ACM paper was that users could select items from pie menus, in certain instances, faster than from linear menus.

Figure 2. Pie Menu

It is commonly known in the clinical psychology literature that we humans are able to juggle about 7 to 12 conceptual frameworks, items, numbers, letters, etc. at the same time. That’s it. Creating a menu with 20 or thirty choices and further depth in hierarchical menus seems ludicrous if your aim is ease of use AND recall.

I would like to see a limit place on the items in menus. After that you must move to a different “platform”/Dialog Box/”Choicer” to see other choices. Now this does smell of the old nemesis “mode”. As a theoretical user interface component “mode” has been taking a bad rap for over twenty years. There was even a T-Shirt (I love T-Shirts) that had the slogan “Don’t Mode Me In”. Cute ..., but the concept of “context” is terribly important to the fast recognition in the normal operation of the human mind. Call it mode or context or frame of reference, but, if you present too many choices too far apart in space, it is very difficult to achieve a “state of fluidity” with the software that approaches what is possible with the incredible user interface of a paint brush in the hand of a Picasso or Da Vinci. That is the ultimate user interface of any tool for the hand of man.( Ok, maybe a piano in the hands of a Tchaikovsky, or a blues song in the throat of a Holiday or James).

Don’t think these issues don’t apply to your CURRENT project. They do ... even if you don’t realize it.

LOW MEMORY CONDITIONS

This is a very technical subject and I hope that the highlights and gloss over job I do here will not offend those who know more that than I do or those that expect this to be the definitive “How to avoid ID -25 in ten easy lessons”. Let’s concentrate on using our “machine” efficiently. It’s rather simple. When you initialize an Application object it requires three parameters. All three are related to low memory conditions. The first one is the extraMasters number. This number determines how many Master Pointer Blocks you want for your application to run. Each Block usually contains 64 master pointers and this is the number of relocatable handles that your application can create WITHOUT creating another Master Pointer Block.

The key is this after “observing your heap zone’s behavior” you determine that you only need 5 Master Pointer Blocks throughout a run of your program. Then it is much better for you to call MoreMasters or in this case send five (5) as the extraMasters value when initializing an Application object. That way the memory manager will not create them whenever it needs them, possibly fragmenting your heap very badly. This is the first and most important Macintosh memory management technique. When the memory manager allocates a Master Pointer Block it just locates free memory and plops one down there ... locked in place forever. The new memory manager is getting smarter, but their are a few bugs...

That phrase “observing your heap zone’s behavior” describes something you can do with MacsBug or TMON. Basically, you can look for the number of non-relocatable blocks that are 264 bytes long (64 pointers times 4 bytes each plus an 8 bytes for the block header). Add 5 and use that number to initialize your Application objects.

The next parameters are the “aRainyDayFund” and the “aCreditLimit”. The description in the manual is adequate (page 230). These are different than extraMasters because extraMasters is a direct Mac ToolBox issue. These parameters require that we again use the “machine/class library” the way it was intended. There is a special logic that must be followed. Let’s say that you allocate 50K for the rainy day fund and set your credit limit at 10K. This means that when the Mac memory manager cannot fill a memory request then the “GrowMemory()” message is sent to the Application object by way of the “GrowZoneFunc” in the CError Class. Actually, the global variable “gApplication” is sent the message. You MIGHT have someone, an object, in gApplication that responded to this message and passed all other messages to the ACTUAL application, but what a hack... The default GrowMemory message handlers in the Application object will first ask if there are temporary buffers that can be eliminated by sending itself the message “MemoryShortage()”. If that doesn’t work then the object performs thusly:

ADoes the Rainy Day fund still exist?

 1 If yes, then can I borrow from rainy day (Is “loanApproved” true?)
 OR
 is what I’m asking for within my “creditLimit” (loanApproved can be 
false in that case)
 
   A  if yes, then proceed to determine if my request will leave me with 
a MINIMUM_BALANCE. MINIMUM_BALANCE is a constant with a normal value 
of 2048 bytes.

 1 if yes, then give us everything NOT just the requested amount. Holding 
the minimum in reserve.

 2 if no, then can I satisfy the request and leave NO reserve.

    A If yes, then do it you fool. Liquidate the fund. Cash in the chips, 
etc. The user is so innocently unaware.(he he he)

    B If no, then Do (A) and hope for the best. Watch Out Below. There’s 
likely to be a crash. Save the Documents and Disk files first.

    B if no, then loanApproved is false and I’m asking for something over 
my credit limit. (Bad boy) So, now I resort to (B) below.

 2 if no, then I do (B) below.

BIf no, rainy day fund is gone then if this request is OK to fail then 
I just return with that information, otherwise I try one last resort 
the message handler “OutOfMemory()”. 

This message handler is more of an bourgeois apologist than a real blue collar proletariat. It flashes some kind of conciliatory message about being out of memory and then waits for another event. What a wimp!

Ok. Whew! Now you know everything you need to know about low memory conditions. If you don’t like what you’ve just read then override it. The whole point of all these games with rainy day fund and credit limit is to establish a “problem domain lexicon” or a set of jargons and objects that you can manipulate and think about as you establish the low memory policies for your application. If you build on and enhance the “reality” of the established lexicon. “Loan Memory to short-lived objects”, “Establish interest on especially hoggy processes” (read - tell the user that this is inadvisable unless they want to burn memory or wait forever). Build on the illusion and you will be able to share your code with your colleagues and talk about concrete things (and charge your enemies obscene interest).

PRINTING

Printing is one of the most complex tasks on the Macintosh and I will provide just an advance on the territory. What TCL provides is a very simple model. Every printer is a Quickdraw device with a border and a margin and dot density. The dot density can be determined along the horizontal or the vertical axis and allows for a measure of proportional control to rest in the application, rather than for instance trusting a printer to print your circle perfectly circularly; you can adjust drawing routines.

In the CPrinter class the access method GetPageInfo provides this information and is the only general routine for interrogation of the state of the “printing engine”. You would only call this routine early in the print cycle or before printing actually began. Calling this routine during printing will confuse the Macintosh printer drivers.

So we start out by having a document that is active that we want to print. When initializing this document we would have indicated if it was “Printable”. If so we would have initialize the instance variable “itsPrinter”, “pageWidth”, and “pageHeight”. Having done this the document is ready to print.

We select the “Print” command from a menu or in some other way indicate that we want to print this document. Normally a direct command “cmdPrint” will then be handled by the DoCommand message handler of a document and will send the message “DoPrint()” to the printer object in the “itsPrinter” instance variable of a document object. Every document can have a different way of responding to the “Print” command by having a different “kind” of object or subclass of CPrinter in its “itsPrinter” instance variable.

DoPrint’s normal behavior is to display the standard PrJobDialog as shown in the figure.

Figure 3. PrJobDialog Imagewriter

If the user click’s OK then DoPrint sends the message PrintPageRange to itself. PrintPageRange is the workhorse that does the actual printing. It opens a print Document (toolbox kind not TCL kind) and sends the message “PrintPageOfDoc” with a page number for every page to be printed. Either 1 or 1 2 3 or 1 2 3 4 5.

Your Document’s PrintPageOfDoc routine must then “draw” on this printer page just as any quickdraw “page/screen”. Actually, it sends, by default a message “PrintPage()” to its main pane. This decouples printing between the Document/Window and the Panes within them.

The PrintPage message handler in CPane is very primitive by itself. It just draws the entire pane using standard quickdraw calls. These calls are bracketed by calls to “PrOpenPage” and “PrClosePage” with the Macintosh PrintPort as the only parameter. This is a toolbox level detail that I don’t want to dive into, but at this stage you would have to override PrintPage in CPane to make many interesting things happen like multi-page printing. To do that you would just have to actually use the parameter pageNum in PrintPage. With that parameter you would interrogate your internal data structures and “draw” the “pageNum” page.

This is not in my opinion an advance in the state of printing abstractions, and I would say that here, in this programming domain, that TCL is the weakest in terms of the available conceptual tools. This does seem to be a weak area in MacApp as well. Data structures and printing. These two areas need more attention from the OP community.

BACKGROUND TASKS

With the advent of Multifinder, it has become more important that an application make active use of the time when it is not directly interacting with the user. Sometimes this is as simple as saying “Hey operating system, here’s some time I don’t need”. Cooperative Multitasking is much like volunteer fire departments. It works in low pressure, low use situations. When applications are “well behaved” and courteous to one another everything is fine. When they are nasty and only execute GetNextEvent, then the whole system is at risk. The solution to this dilemma is known as Preemptive Multitasking. That would not allow an application to “hog” the CPU any longer than necessary, else they would be swapped out and allow someone else to run by force. Couple that with hardware memory protection and you’ve got a real ball game on your hands. I can’t wait.

TCL allows two types of things to be scheduled for regular attention. One type is the “idleChore” these are arranged in a list and perform their function during the time when there are no Macintosh system events directed at the application. (User is thinking, not typing).

The other is the “UrgentChore” which is arranged in a cluster and is performed after every event (even idle’s), if and only if the “urgentsToDo” Application object instance variable is true. After urgents are done then the urgentsToDo variable is set to false.

The default Application “Idle” message handler is the seed of much activity in TCL. First it checks to see if the rainyDayFund is used up. If so, it tries to replenish the fund, and if it is not successful gives the user an unpleasant warning/feeling.

Next it does something that is quite neat. It gives each “Bureaucrat” in the chain of command a chance to do something by sending a “dawdle” message up the chain, starting at the gopher. If you remember your chain of command, this is an very nice time to do perhaps user timers. The Application object gets a “dawdle” message too.

Last but not least, each object in the IdleChore list is asked to “perform”. That is that each object is sent the “perform” message and a parameter which represent the largest signed long integer possible. I don’t know why. Of course the classical use of the idleChore is to flash the cursor in a TextEdit field. The default dawdle for TextEdit is in CEditText.

One last thing about idle chores. Since they are in a CList class data structure they can be ordered in a variety of interesting ways. (By Need?, By Kind?, By Appropriateness?) This is a glimpse of the power of flexible data structures.

Urgent chores are more mysterious. These chores are not arranged in a list (CList) per se. They are arranged in a cluster (CCluster). The difference is very subtle. Urgent chores are done once, and then have to be “scheduled” again by some object. The speed of access may be one explanation for the CList vs. CCluster difference, frankly, I don’t know. One thing is for sure, this is a powerful way of adding things to the “computational sequence” without actually performing the operation yourself.

Any object can in effect say “I want this to be done as soon as it is practicable”. The TCL decides when that time comes and does it. Very Nice. Imagine a lot of calculations you want done but you want the user to be able to continue to select new ones. Build a list and execute them “en masse”? Or just send the message “AssignUrgentChore()” for each calculation to the Application object which is in the global gApplication at all times. Simple.

I really don’t have much else to say on this subject. Architecturally, Urgent chores and Idle chores are extra pipe in the walls for cables you might want to lay in the future. Inter Application Communication (IAC) in System 7.0 should make features of this nature more used and abused. I hope its not a nightmare. Is this parallel processing? Not quite, but the flavor is very interesting on the programming palette.

ERROR HANDLING

Error Handling is the last subject we shall cover and the pundits are already saying that error handling should be the FIRST consideration. Well, in the old days, of procedural programming that MAY have been true. In the present day of OP programming error handling, as opposed to logical errors and bad pointers, is rather trivial. One of the advantages of OP is that only certain kinds of errors are possible. Of course we need to remember the strict distinction between the world inside the application, but outside methods/routines/handlers. In this in-between world there is order and orderly progression. This is the “machine” that passes messages and responds to them, broadcasts them and eats them. The world inside methods/routines is the old free for all that we’re quite used to. A stray pointer can bring down the whole party.

The first concept is to separate your error handling schemes for the world inside methods/routines and the world outside. Once that is done you’ll feel better. Next let’s take a look at the CError class. It is three pages in the manual. I won’t repeat those pages here (pages 313, 314, 315). Basically, to really do sophisticated error handling you will have to create a subclass of this class. The functions provided are good solid building blocks.

When the Application object is initialized an instance of CError is created and stored in the global gError. The manual says “you can” create a subclass of CError for more sophisticated error handling and I’m saying “you must” do so.

The only problem here is that the default Application initialization routine takes on so much that at this point (needing sophisticated error handling) you have to replace/override the entire routine. You can’t just do the neat stuff and then call the inherited method. Why? Because it then proceeds to fill GLOBAL variables with stuff, like gError.

I don’t know what the solution is here, but this ain’t it. Again separate your error handling for the two worlds and you will find very few errors in the final product can possibly slip through.

SUMMARY

When you read “and XYZ sends a ‘message’ to JKL” you may say to yourself “Why use all this fancy language, when its plain that one “function” calls another “function”. Well, the answer is more shadow than substance at the moment. Even for those that claim great successes with OP, these are largely research projects or projects that are under similar kinds of performance pressures. The pressures of the “real” world, business and defense programming are slowly using many of these techniques, but most organizations are still just at the talking stage.

Also the technology is somewhat limiting. OP without multiple inheritance (MI) which neither MacApp or TCL have (They have Single Inheritance (SI) just as Smalltalk does) is not unanimously considered the most complete model for OP. Larry feels differently, and I acknowledge that there is nothing you can accomplish with MI that you cannot accomplish with SI. A similar argument can be made for assembly language vs. C or Pascal.

In fairness, Larry can be ever so persuasive in person and in writings. The real story is unfolding here in your hands. YOU ... yes YOU ... All of those who read this article are now right-smack-dab in the middle of what I believe to be one of the great historical times in the saga of “artificial computation”. Read everything you can get your hands on and hold on ... It’s the Century of OP.

The summary is really a moral.

Use what you got.

Learn the class library of your choice. Learn how to change and improve the behavior from within the design framework. If you seriously disagree with the structures or arrangement of MacApp or TCL ... AND you try to do something about it; you will quickly find yourself on the BLEEDING edge and looking for a high slippery ledge. Another way of saying this is, if you don’t get the “program”, class library concept, don’t attempt to use it.

Too much overriding is hazardous to the health of your programs.

Another thing ... If you are developing classes that are generally useful in MacApp or TCL and you don’t release them to the public, either for a fee or gratis, you will be hunted down and fed to the great blue programming dragon. The age of OP is one of giving, sharing and building on Other Peoples Work (OPW). Just do it.

It will come back to you a hundred fold. I know this sounds ... well sappy ... but it does work. Let’s all stop working so hard ... OK??? Where are those entrepreneurs who steal ... I mean “merchandise” honest programmer’s work and sell it for “Distribution Charges”. Why don’t we have a “Reusable Code CD-ROM”? $59.00 Cash or Visa/MC. Source code is better than an application any day of the week.

Count those cycles ... I mean messages.

References

1 Introduction to MacApp 2.0 and Object-oriented programming, Apple Computer, Inc.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.