This section discusses how your part editor allocates and releases OpenDoc objects, and how it should release unneeded memory when requested to do so by OpenDoc.
Any OpenDoc object that you instantiate must be created by a factory method, a method of one class used to create an instance of another class. You should never use C++ new operator, for example, to create an object of an OpenDoc class (except for subclasses, such as ODExtension, through which you extend OpenDoc and for which your part is the factory).
Table 12 lists the factory methods you
should use to instantiate any of the OpenDoc classes whose objects
your part editor might ever need to create.
The table lists methods that create new objects as well as those
that return previously
created objects, whether currently in memory or stored persistently.
Your own part editor has the factory methods for the classes
ODEmbeddedFramesIterator and any of its own extension objects.
ODDraft has the factory method for ODPart and
any of its subclasses, including your part editor.
Table 12. Factory Methods for OpenDoc Classes
Object to be Created | Class and Factory Method |
---|---|
ODCanvas | ODFacet::CreateCanvas ODWindowState::CreateCanvas |
ODContainer | ODStorageSystem::CreateContainer ODStorageSystem::AcquireContainer |
ODDesc | ODNameResolver::GetUserToken |
ODDocument | ODContainer::AcquireDocument |
ODDraft | ODDocument::CreateDraft ODDocument::AcquireDraft |
ODDragItemIterator | ODDragAndDrop::CreateIterator |
ODEmbeddedFramesIterator (subclass) | ODPart::CreateEmbeddedFramesIterator |
ODExtension (subclass) | ODObject(subclass)::AcquireExtension (usu.ODPart(subclass)::AcquireExtension) |
ODFacet | ODFacet::CreateEmbeddedFacet ODWindowState::CreateFacet |
ODFacetIterator | ODFacet::CreateFacetIterator |
ODFocusOwnerIterator | ODArbitrator::CreateOwnerIterator |
ODFocusSet | ODArbitrator::CreateFocusSet |
ODFocusSetIterator | ODFocusSet::CreateIterator |
ODFrame | ODDraft::CreateFrame ODDraft::AcquireFrame |
ODFrameFacetIterator | ODFrame::CreateFacetIterator |
ODLink | ODDraft::AcquireLink |
ODLinkIterator | ODDraft::CreateLinkIterator |
ODLinkSource | ODDraft::CreateLinkSource ODDraft::AcquireLinkSource |
ODLinkSourceIterator | ODDraft::CreateLinkSourceIterator |
ODLinkSpec | ODDraft::CreateLinkSpec |
ODMenuBar | ODMenuBar::Copy ODWindowState::AcquireBaseMenuBar ODWindowState::AcquireCurrentMenuBar ODWindowState::CopyBaseMenuBar |
ODNameSpace | ODNameSpaceManager::CreateNameSpace ODNameSpaceManager::HasNameSpace |
ODObjectIterator | ODObjectNameSpace::CreateIterator |
ODPart (subclass) | ODDraft::CreatePart ODDraft::AcquirePart |
ODPlatformTypeList | ODStorage::CreatePlatformTypeList |
ODPlatformTypeListIterator | ODPlatformTypeList:: CreatePlatformTypeListIterator |
ODPopup (for OS/2 and Windows) | ODPopup::CopyPopup ODWindowState::CopyBasePopup |
ODSettingsExtension | ODPart::AcquireExtension |
ODShape | ODShape::Copy ODShape::CopyFrom ODShape::NewShape ODFrame::CreateShape ODFacet::CreateShape |
ODShellPlugIn | ODPart::AcquireExtension |
ODStorageUnit | ODDraft::CreateStorageUnit ODDraft::AcquireStorageUnit |
ODStorageUnitCursor | ODStorageUnit::CreateCursor |
ODStorageUnitRefIterator | ODStorageUnit::CreateRefIterator ODStorageUnitView::CreateStorageUnitRefIterator |
ODStorageUnitView | ODStorageUnit::CreateView |
ODTransform | ODTransform::Copy ODTransform::CopyFrom ODTransform::NewTransform ODFrame::CreateTransform ODFacet::CreateTransform |
ODTypeList | ODStorageSystem::CreateTypeList |
ODTypeListIterator | ODTypeList::CreateTypeListIterator |
ODOValueIterator | ODOValueNameSpace::CreateIterator |
ODWindow | ODWindowState::RegisterWindow ODWindowState::RegisterWindowForFrame |
ODWindowIterator | ODWindowState::CreateWindowIterator |
The use of reference-counted objects is part of the OpenDoc memory management scheme. Reference-counted objects maintain a count of the current number of references to them; that is, they are shared objects that are aware of how many other objects are making use of them at any one time.
During an OpenDoc session, many objects are created. Because there can be a very complex relationship among objects, it might be difficult for a part or for OpenDoc to determine when it is safe to delete an object from memory.
reference-counting is a way to determine when a run-time object can be deleted, so that valuable memory space can be reclaimed.
A reference count is 0 or a positive integer. A value greater than 0 means that at least one reference to the object currently exists, and thus the object must not be removed from memory. All descendants of the class ODRefCntObject and any of its subclasses (including frames, links, link sources, and parts) are reference-counted.
Each reference-counted object is created through a call to its factory method, the method (usually in another class) responsible for creating that object and initializing its reference count. For example, a draft object is created by calling the CreateDraft or AcquireDraft method of a document object. Likewise, a frame object is created by calling the CreateFrame or AcquireFrame method of a draft object. See Table 12 for a list of factory methods.
These are the reference-counted objects:
When it is first created by its factory object, a reference-counted object has a reference count of 1. Thus, the frame object returned by a draft's CreateFrame method always has a reference count of 1, because that method always creates a new object. The frame object returned by a draft's AcquireFrame method, however, may have a reference count greater than 1, because that method may return a reference to a preexisting frame.
Calling the Release method of a reference-counted object decrements its reference count; calling its Acquire method increments its reference count. Calling the Release method of a reference-counted object when its reference count is 1 causes its reference count to be set to 0 and may result in the object being deleted from memory. It is an error to access an object whose reference count is 0.
Each reference-counted object stores its own reference count and returns it to callers of its GetRefCount method. When the object's reference count goes to 0, the object is responsible for ensuring that it is removed from memory, either by deleting itself or by notifying its factory object which will remove it from memory.
Whenever your part editor writes a reference to a reference-counted object into a data structure, it should increment that object's reference count by calling its Acquire method. When your part editor is finished working with that object, it should call the object's Release method.
Because shape and transform objects are reference-counted and are widely used, it is important to remember always to release them instead of deleting them. Any method call with which you acquire a shape or transform must be balanced by a subsequent call to Release.
clipShape = facet->AcquireClipShape(ev, biasCanvas); . . . clipShape->Release(ev); clipShape = kODNULL; |
Likewise, any method call that assigns a reference-counted object to another object increases its reference count. Therefore, you can immediately release your own reference to it.
newShape = facet->CreateShape(ev); . . . facet->ChangeGeometry(ev, newShape, transform, biasCanvas); newShape->Release(ev); newShape = kODNULL; |
Acquire versus Get
Methods whose names begin with Acquire increment the reference counts of the objects they return; methods whose names begin with Get do not. Every call to ODDraft::AcquireFrame, for example, must be balanced with a call to ODFrame::Release. A call to Facet::GetCanvas, on the other hand, requires no corresponding call to decrement reference count.
As a reference-counted object, your part is responsible for implementing an override of the Release method and for calling the ReleasePart method of its draft object when your part's reference count goes to 0. Your part does not need to release all of its references or do any other shutting down or deallocation at that point; the ReleasePart method calls your part's destructor, which should take care of deallocation. However, your part could get rid of unneeded structures or services before calling ReleasePart. For example, a communications part editor may choose to close its driver as soon as its reference count reaches 0.
If your part has a reference count of 0, but calling your draft's ReleasePart method has not resulted in your part's destruction, the draft object can retrieve and reuse your part. In that case, any structures or services you deallocated when the reference count went to 0 must be reallocated. Your Acquire method can perform those tasks for the case in which the reference count goes from 0 to 1.
Your part is destroyed after its ReleaseAll method is called, regardless of its current reference count, when its draft closes. See "Closing Your Part" for more information.
Testing objects for equality
If you need to compare two existing OpenDoc objects for equality, don't simply compare their pointers. Instead, call the IsEqualTo method (defined in ODObject) of either object. IsEqualTo always gives the correct result, even in a distributed environment when comparing pointers may fail. However, be sure never to call the IsEqualTo method of a null object reference.
Many parameters to OpenDoc methods consist of references to objects or to other data that needs special attention in terms of allocating and releasing the storage associated with it. This section discusses parameter handling for byte arrays, strings, and objects.
To make the OpenDoc programming interface CORBA-compliant and capable of distributed execution, OpenDoc requires you to use a certain format when passing method parameters that point to buffers containing variable-length data.
OpenDoc defines the ODByteArray structure as a sequence of octets (unsigned 8-bit values). It consists of a buffer size field, a data length field, and a pointer to the buffer associated with the structure. All variable-length data is passed to or from OpenDoc with byte arrays. For example, the storage-unit methods GetValue and SetValue use byte arrays for the data being passed.
The caller of a method that takes variable-length data must place the data in a buffer pointed to by a byte array; the receiver of the data then retrieves the data from the buffer and uses it. Both sender and receiver must understand the underlying type of the data. For example, if your part calls SetValue, your part passes a structure of type ODByteArray, but the method understands the implied type of the contents of the buffer (a storage-unit value).
If you are the caller of a method that uses a byte array as a parameter, follow these rules for allocating and releasing the array:
All data passed by means of a byte array must be self-contained; for example, it can't contain pointers to data outside of itself.
If you are the caller of a method that uses a parameter that is a string (type ODISOString or one its equivalents, such as ODValueType) or an OpenDoc object, similar rules apply:
Your part does not necessarily have to maintain frame objects in memory at all times for all of its embedded frames and display frames. You may have many display frames or embedded frames, only a few of which are visible at any one time. In such a case, you might want to reduce memory use by creating frame objects only for those frames that are currently visible. Then, as the user scrolls through your part or resizes it, or brings other display frames of your part into view, you can create frame objects for frames that appear, and release frame objects from frames that disappear. This process, which could be called lazy instantiation, works like this: