TweetFollow Us on Twitter

March 97 - The OpenDoc Road


Making the Most of Memory in OpenDoc

Troy Gaul and Vincent Lo

In Issue 28, we discussed how the OpenDoc Memory Manager works and how part editors manage Toolbox memory. This time we'll examine ways to use memory more efficiently in the OpenDoc environment.

We'll begin by talking about how to avoid memory leaks. Memory leaks, which can be a problem when developing traditional Macintosh applications, are as much a concern in OpenDoc. But because OpenDoc uses reference counting, there are a few extra things to pay attention to. We'll also discuss how to handle parameters correctly to avoid memory leaks, and we'll take a look at ways you can set up your part editor to maximize memory usage.


OpenDoc objects and part editors use a reference-counting scheme that enables OpenDoc to keep track of which objects are in use. Each time a client acquires an object (through the object's Acquire method), the object's reference count is incremented by 1. When the object is no longer being used, the client releases it (by calling the object's Release method) and its reference count is decremented. The object's reference count indicates how many references to the object are being held by clients. When the reference count goes down to 0, the object can be destroyed without affecting any other objects. For more information on how reference counting works in OpenDoc, see the OpenDoc Road column in develop Issue 27, "Facilitating Part Editor Unloading."

If the acquired object doesn't get released when it should, the reference count doesn't go to 0 and the object remains in memory until the session ends. As a result, a memory leak occurs because the occupied memory can't be used during the session.

To avoid reference count errors, it helps to keep in mind which classes are reference-counted and which methods affect an object's reference count. OpenDoc uses reference counting on classes whose objects often have more than one client. These classes are subclasses of ODRefCntObject, and many are classes that part editors interact with directly.

In general, if a method name starts with "Acquire," the reference count of the object named in the method is incremented when the method is called. When the object is no longer needed, the caller should release it. For example, if a part editor calls ODDraft::AcquireFrame to access a frame object, the reference count of the returned frame object is incremented. After the editor is done using the frame reference, a call to the object's Release method (ODFrame::Release) must be made to avoid a memory leak.

Some methods return a reference-counted object without affecting the object's reference count. These methods usually start with "Get." For example, ODFacet::GetFrame returns the frame object with which the facet is associated without incrementing the reference count of the frame object. In this case, the caller shouldn't call ODFrame::Release. Typically, a Get method is used to return an invariant or unchanged attribute of an object. In the case of ODFacet, the facet acquires and stores a reference to its ODFrame object. This reference isn't released until the ODFacet object is deleted. When ODFacet::GetFrame is called, ODFacet returns the stored reference to the caller. Since this reference remains valid until the ODFacet is deleted, you can use it as long as the ODFacet is a valid object. If you want to use the returned ODFrame object beyond the ODFacet's lifetime, you should call Acquire on the ODFrame to ensure that you have a valid reference to it.

The best way to avoid reference count errors is to familiarize yourself with the OpenDoc API and understand how it affects an object's reference count. The OpenDoc Class Reference provides a detailed description of reference counting for each method.

Temporary objects to the rescue. The code for acquiring a reference-counted object for a brief period of time and then releasing it turns out to be quite complicated. Listing 1 shows how complicated it can be to handle a reference-counted object when using exception-handling code.

Listing 1. Handling reference-counted objects

ODFrame* frame = kODNULL;
// Make sure that the frame can be used in
// the CATCH block.
   // Acquire the frame.
   frame = draft->AcquireFrame(ev, id);
   // Do something with the frame here.
   // Release it when done.
   if (frame)

To help alleviate this problem, OpenDoc provides a utility library that uses stack-based C++ objects to wrap references to OpenDoc reference-counted objects. These C++ objects are called temporary objects. Whenever such a C++ object goes out of scope, its destructor is called and releases the reference-counted object.

The code fragment shown in Listing 2 does the same thing as the example in Listing 1 but uses temporary objects instead. This code is simpler and less error-prone.

Listing 2. Easier handling of reference-counted objects

   TempODFrame frame =
         draft->AcquireFrame(ev, id);
   // Do something with the frame here.

The OpenDoc utility library provides temporary objects for 17 reference-counted classes, including ODPart, ODFrame, ODExtension, and ODStorageUnit. For more information on creating temporary objects, see the "Temporary Objects" section of Appendix A in the OpenDoc Cookbook.

The OpenDoc utility library also provides temporary objects for objects that aren't reference-counted, such as ODByteArray and ODIText. These OpenDoc types deserve special attention in regard to memory usage.

The ODByteArray structure contains three fields: _buffer, _maximum, and _length. The _buffer field points to a memory block whose size is indicated by the _maximum field. _length is the number of bytes used; it has to be less than or equal to the value of _maximum.

Generally, ODByteArray is used instead of a raw pointer because the size of the memory block is included. This enables SOM and OpenDoc to pass data between processes without relying on shared memory. But because the _buffer field is hidden in the ODByteArray, the memory block can easily be forgotten. Failing to free this memory block when an ODByteArray is deallocated creates a memory leak.

The ODIText structure stores a user-visible string. One of its fields contains the string's format; the other is an ODByteArray that contains the text string. The memory block in the ODByteArray needs to be freed when the ODIText structure is deallocated.

Handling in and out parameters. Memory leaks can also occur when parameters aren't handled correctly. In an OpenDoc method, each parameter is designated as in, out, or inout.

  • An in parameter passes data from the caller to the callee.
  • An out parameter transfers data from the callee to the caller. A method's result also acts as an out parameter.
  • An inout parameter passes data from the caller to the callee, which can then modify it and pass it back.
To determine a particular parameter's designation, you can check the ".idl" files, or see the OpenDoc Class Reference for detailed information on each parameter.

The parameter's designation defines the memory responsibility of the caller and callee. The part editor can use memory on the stack for parameters of primitive types or fixed-size data structures. But for strings, byte array buffers, and objects, the part editor must use the OpenDoc Memory Manager to do the following:

  • allocate and deallocate memory for in and inout parameters passed to an OpenDoc object
  • deallocate memory for out parameters returned from an OpenDoc object
  • allocate memory for out parameters returned from the part editor's methods
If a part editor calls an OpenDoc method and doesn't deallocate the out parameter, the memory won't be freed until the session ends, causing a memory leak.

Since it's impossible to know how a piece of memory is allocated, OpenDoc and part editors have to use the OpenDoc Memory Manager as the common memory management facility. This is the only way to ensure that memory allocated by OpenDoc can be freed by the part editor and vice versa.


Because your part editor is used in documents with other part editors as users construct compound documents, it's important to make the best use of memory. Let's talk about some of the things you can do to minimize memory usage.

Keep the data section small. When creating a part (which is a shared library), the linker will generate code and data sections. The code section contains the instructions that make up your part editor. This section is read-only because it's file-mapped onto read-only memory when virtual memory is in use. The data section is stored separately because it needs to be writeable; it contains globals, static variables, transition vectors, virtual tables, and so on.

A single in-memory or memory-mapped copy of the code section is shared by all processes in which that code is used. The data section is handled differently: Each process instantiates a copy of the data section, making globals per-process rather than per-computer or per-part. Also, because there is normally one process per OpenDoc document, a separate data section usually exists for each document that contains a part bound to your editor. Therefore, you should control the size of your data section so that copies of it don't take up too much memory when multiple documents are open.

Here's what you can do to keep the size of your data section down:

  • Limit your use of global variables -- Since globals are stored in the data section, use them only for those things that must be per-process globals.
  • Use read-only string constants -- String constants that are writeable must be located in your data section because the compiler assumes that you might write into the memory associated with them. If you have the compiler make your string constants read-only (by checking the Make Strings ReadOnly box in the PPC Processor panel in CodeWarrior, for example), these strings can be put into the code section instead of the data section. But remember that after doing this, you should not write to these string constants. You can still allocate memory in the OpenDoc heap for strings and write to them. You can also put string buffers, such as Str255 strings, on the stack in your code and write to them there. Note that any user-visible string constants should be stored in resources so that your editor can be localized.
  • Avoid virtual functions -- Space is made for virtual functions of C++ classes in the data section because the virtual tables must be written once (for each process) to point to the functions residing in the read-only code section. You can make the virtual table smaller by not making functions virtual. It's best to design your classes with as few virtual functions as possible, adding more only as the need arises.
  • Reduce the number of transition vectors -- For each import and export symbol in a library, there's a TVector, or transition vector. (CFM-68K calls them XVectors.) The TVector must be writeable because, like a virtual table, it has to be written once to point to the corresponding memory address when the code is loaded.

Reduce exports. By minimizing the number of symbols that are exported, you can save memory. Symbol name strings are stored in the PEF (Preferred Executable Format) container of your code fragment. If you're using a shared library that contains a framework or set of C++ classes, you usually need to export the symbols for each of the member functions in the shared library and import the relevant ones into your part editor to call them or subclass them. C++ functions have long symbol names because they include type signature information. As a result, the size of your code fragment can increase significantly.

This is particularly noticeable if you have multiple part editors that reference the same code, since they'll all have large tables of symbol names. You can use a tool like DumpPEF to check what type of information your code fragment contains and how much space it's taking up.

A workaround is to statically link classes to your part editor. This means fewer imports and less memory used. Of course, if you do this, you lose some of the advantages of sharing code via a shared library.

Package multiple part editors intelligently. If you're writing a suite of part editors for end users, it's a good idea to package them as separate editor files in the Editors folder. However, as mentioned earlier, if you want to share common code, the amount of memory that's used by all the editors combined can be substantial.

Packaging all your part editors and the common code in a single code fragment reduces the number of imports and exports to almost nothing. But then you can't update just one of the editors in your suite -- you have to replace the entire shared library. It's also detrimental if only one or two of your editors are being used, because the system loads the entire code fragment but only a portion of it is being referenced. This isn't as much of a problem if the user has virtual memory enabled, but without it, memory is wasted.

To combine multiple OpenDoc part editors into one code fragment, you have to compile the code for all of them together. You can do this either by putting them all into one project or by having multiple projects generate static library files and a master project that includes each of the single-part libraries. Then you need to make sure that the ClassData symbols for all the parts are exported as separate symbols, by using pragmas or a ".exp" file. Finally, you must include the 'nmap' resources from all the individual editors in the combined file. Of course, the IDs of these resources can't conflict, but since OpenDoc doesn't require any specific resource IDs for 'nmap' resources, that shouldn't be a problem to set up.

Use SOM classes instead of C++. If you'd like to separate framework code from part editor code (for example, to have multiple editors share the same framework or set of classes), note that there are several advantages to using SOM classes instead of C++ classes.

With SOM on the Mac OS, you only have to export the class's ClassData symbol. The virtual tables are maintained by the SOM kernel; they don't exist in your data section.

Since SOM has so little overhead, you can package multiple editors as separate code fragments (either in separate, replaceable files or in a single file). Editors that aren't being used won't be loaded.

You can also reduce the granularity of your shared libraries, such that different classes are in different shared libraries (again, in separate files or the same file). This allows you to split up your framework so that only the sections that the client needs are loaded into memory. For example, if you have one part editor that embeds other editors and another that doesn't, but they share the same framework, the framework's embedding code can be in a separate shared library from the code that's needed by all part editors.

SOM supports release-to-release binary compatibility, and it deals with the fragile base class problem of C++. It also defines a binary interface that supports languages other than C++. Currently, emitters on the Mac OS for C and C++ are available. Other languages can also be supported.

Don't be afraid to use SOM. Better tools for building SOM classes are being released. In particular, Direct-to-SOM support has been added to Metrowerks CodeWarrior's C++ compiler and Apple's MrCpp, so you can build SOM classes with much less effort and with a more familiar syntax.

Use #pragma internal. Space is also wasted when it's set aside for instructions and never used. By default, functions on a PowerPC(TM) processor are assumed to be external, so for the processor to jump to the routine, it's expected to go through a TVector. To do this, the compiler leaves room in the code for the linker to add the necessary instruction to restore the TOC (Table Of Contents) after a jump to a TVector. If it turns out that the routine is in the same code fragment, restoring the TOC isn't necessary, but because the space has already been inserted, the linker has little recourse but to put a no-op instruction in that place. (The code was generated expecting certain offsets, so the linker can't shuffle the code around easily.) Space is then wasted for calls that are internal to the code fragment.

There are a couple of ways around this. One thing you can do is to declare a function with the keyword static (this means that it can't be used outside the file it's defined in) so that the compiler can tell it's an internal function. You can also use the internal pragma in CodeWarrior. The following code marks the declaration of two functions as internal by enclosing them in a #pragma internal block. This informs the compiler that calls to those functions can be assumed to be internal calls, and it won't leave space for restoring the TOC after a TVector call.

#pragma internal on
   void InternalFunction();
   ODBoolean AnotherInternalFunction(short count);
#pragma internal reset
Note that a function internal to your code can still be an export from your shared library. In this case, your header should conditionalize its inclusion of #pragma internal for your own use so that external clients don't mistakenly see it as internal.
    This technique is not used for CFM-68K code because calls there are assumed to be internal unless they're marked otherwise (with #pragma import).*
The MrPlus profiling tool can also be used to get rid of unneeded no-op instructions. You can get this tool and its documentation on the E.T.O. and MPW Pro CDs.


OpenDoc presents a new model for constructing software. However, many of the techniques you've used for the traditional application model can still be applied to the OpenDoc environment. By also incorporating some of the suggestions we've brought up here, you'll be able to further reduce your part editor's footprint and avoid memory leaks.


    This documentation is available on the OpenDoc Developer Release CD and on the OpenDoc Web site (

    • OpenDoc Programmer's Guide for the Mac OS by Apple Computer, Inc. (Addison-Wesley, 1995). The OpenDoc Class Reference for the Mac OS is provided on a CD that accompanies this book.
    • OpenDoc Cookbook for the Mac OS by Apple Computer, Inc. (Addison-Wesley, 1995).

TROY GAUL ( recently joined the OpenDoc engineering team, where he's working with JavaTM. Having also written the sample part editor formerly known as Cappuccino, he has a caffeine buzz that should last into the next century.*

VINCENT LO ( is Apple's technical lead for OpenDoc. Since he recently introduced the OpenDoc team to Hong Kong cinema, it occasionally happens that the OpenDoc engineering meeting resembles a scene from a Hong Kong action movie.*

Thanks to Jens Alfke, David Bice, and Steve Smith for reviewing this column.*


Community Search:
MacTech Search:

Software Updates via MacUpdate

1Password 6.3.5 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
QuickBooks R9 - Financial ma...
QuickBooks helps you manage your business easily and efficiently. Organize your finances all in one place, track money going in and out of your business, and spot areas where you can save. Built for... Read more
TextMate 2.0-beta.12.25 - Code/markup ed...
TextMate is a versatile plain text editor with a unique and innovative feature set which caused it to win an Apple Design Award for Best Mac OS X Developer Tool in August 2006. A rapidly growing... Read more
TunnelBear 3.0.4 - Subscription-based pr...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
PDFpen 8.2.1 - $74.95
PDFpen allows users to easily edit PDF's. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Features... Read more
PDFpenPro 8.2.1 - $124.95
PDFpenPro allows users to edit PDF's easily. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Create fillable forms and tables of content... Read more
Cosmo Run 2.5.1 - Arcade game with ever-...
Cosmo Run is an arcade game with ever-changing aspect of the play-world. Survive long enough and you will meet special alternative paths - both hardcore and rewarding. Are you worthy to command the... Read more
Box Sync 4.0.7724 - Online synchronizati...
Box Sync gives you a hard-drive in the Cloud for online storage. Note: You must first sign up to use Box. What if the files you need are on your laptop -- but you're on the road with your iPhone? No... Read more
VueScan 9.5.59 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Skype - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more

Latest Forum Discussions

See All

5 mobile strategy games to try when you...
Strategy enthusiasts everywhere are celebrating the release of Civilization VI this week, and so far everyone seems pretty satisfied with the first full release in the series since 2010. The series has always been about ultra-addictive gameplay... | Read more »
Popclaire talk to us about why The Virus...
Humanity has succumbed to a virus that’s spread throughout the world. Now the dead have risen with a hunger for human flesh, and all that remain are a few survivors. One of those survivors has just called you for help. That’s the plot in POPCLAIRE’... | Read more »
Oceans & Empires preview build sets...
Hugely ambitious sea battler Oceans & Empires is available to play in preview form now on Google Play - but download it quickly, as it’s setting sail away in just a few days. [Read more] | Read more »
Rusty Lake: Roots (Games)
Rusty Lake: Roots 1.1.4 Device: iOS Universal Category: Games Price: $2.99, Version: 1.1.4 (iTunes) Description: James Vanderboom's life drastically changes when he plants a special seed in the garden of the house he has inherited.... | Read more »
Flippy Bottle Extreme! and 3 other physi...
Flippy Bottle Extreme! takes on the bottle flipping craze with a bunch of increasingly tricky physics platforming puzzles. It's difficult and highly frustrating, but also addictive. When you begin to master the game, the sense of achievement is... | Read more »
Plants vs. Zombies Heroes guide: How to...
Plants vs. Zombies Heroes surprised us all, presenting a deep deck building experience. It's a great CCG that stands up well to the competition. There are a lot of CCGs vying for players' attention at the moment, but PvZ Heroes is definitely one... | Read more »
Arcane Online takes Online RPG’s to anot...
If you think that you need a desktop to enjoy high quality MMO gaming then Arcane Online hopes to prove you emphatically wrong. An epic fantasy Online RPG set in the land of Eldine, Arcane Online offers an abundance of features and content that... | Read more »
It’s time to step up and start your spoo...
So you’ve just downloaded Seekers Notes: Hidden Mystery, and you obviously want to hit the ground running. Well you’re in luck because you just so happen to be reading our very useful guide to playing. First released in July last year, the game has... | Read more »
Diggy’s Adventure launches on Android an...
Diggy’s Adventure is the exciting new mobile app brought to you by Pixel Federation. Transporting you all the way to Egypt, you’ll join Diggy and his mates in their quest to find Diggy’s dear old dad – completing challenges put to you by the gods (... | Read more »
Eggggg - The Platform Puker (Games)
Eggggg - The Platform Puker 1.2 Device: iOS Universal Category: Games Price: $1.99, Version: 1.2 (iTunes) Description: | Read more »

Price Scanner via

Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more
Apple, Samsung, Lead J.D. Power Smartphone Sa...
Customer satisfaction is much higher among smartphone owners currently subscribing to full-service wireless carriers, compared with those purchasing service through a non-contract carrier, according... Read more
Select 9-inch Apple WiFi iPad Pros on sale fo...
B&H Photo has select 9.7″ Apple WiFi iPad Pros on sale for up to $50 off MSRP, each including free shipping. B&H charges sales tax in NY only: - 9″ Space Gray 256GB WiFi iPad Pro: $799 $0 off... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Apple refurbished Apple TVs available for up...
Apple has Certified Refurbished 32GB and 64GB Apple TVs available for up to $30 off the cost of new models. Apple’s standard one-year warranty is included with each model, and shipping is free: -... Read more
15-inch Retina MacBook Pros on sale for $200...
B&H Photo has 15″ Retina Apple MacBook Pros on sale for $200 off MSRP. Shipping is free, and B&H charges NY tax only: - 15″ 2.2GHz Retina MacBook Pro: $1799 $200 off MSRP - 15″ 2.5GHz Retina... Read more
Apple refurbished 2016 13-inch MacBook Airs a...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 2016 13″ 1.6GHz/8GB/128GB MacBook... Read more
Apple refurbished iMacs available for up to $...
Apple has Certified Refurbished 2015 21″ & 27″ iMacs available for up to $350 off MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: - 21″ 3.... Read more

Jobs Board

*Apple* Retail - Multiple Positions- New Yor...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions- Yonkers...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Senior Manager, Security Engineering - *Appl...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more Read more
Manager, Threat Intelligence - *Apple* Info...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more Read more
*Apple* Retail - Multiple Positions- Mishawa...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.