Navigation: Programming Cookbook > Creating COM Components >
A fundamental part of COM is the management of the lifetime of objects by reference counting. This topic is so important (and so error prone) that many introductory COM books (such as Rogerson) dedicate an entire chapter to the subject. It is essential to have a working knowledge of reference count management through AddRef and Release to develop in COM. Even if the programming language or environment you use hides most or all of the requirement to invoke these operations explicitly (as Dolphin does), you will still need to understand the issues surrounding reference counting in order to be a competent COM developer. For example it is quite easy to create cyclic references between objects (one object holds a reference to another, which directly or indirectly holds a reference back to it), and such cycles cannot be garbage collected by reference counting alone.
Dolphin takes care of the majority of COM reference count management, so for the most part explicit reference counting with AddRef/Release is not required. This is achieved by the COM framework incrementing the reference count when necessary, and by taking advantage of the garbage collector’s finalization capabilities to automatically decrement the reference count when interface pointers are no longer needed. Since in Smalltalk we pass around objects by reference, rather than copying them, when we pass around a Smalltalk object that represents a COM interface pointer there is no need to adjust the reference count – we can rely on the garbage collector to manage the multiple references for us, and so we can hold only a single COM reference no matter how many times we reference that interface object in Smalltalk.
Each COMInterface instance has a single reference count associated with it, and is marked as finalizable. When a COMInterface instance is finalized it releases its associated reference count. COMInterface follows the Object Liberation Strategy pattern, so it is also safe to send an instance the #free message to explicitly release it if one knows for certain that it is no longer required (for example when the scope of the interface pointer is local to a single method). Of course if one sends #free to a shared interface pointer then it will be invalidated and become unusable. Although this is safe, and will not cause a crash, it is better to rely on finalization to Release the underlying pointer when you are not sure whether the Smalltalk object is referenced from elsewhere.
There are some circumstances where one needs to perform explicit reference count management. It is essential to have a good understanding of the division of responsibilities for reference count management in COM in order to know when, and when not, to do this. The basic rules are:
|•||The client/caller is responsible for all reference count management for “in” parameters, except in the case where the server captures a reference to an inbound interface pointer.|
|•||When a server captures a reference to an inbound interface pointer, it must AddRef it, and subsequently Release it when it is no longer needed. If it fails to AddRef a captured interface pointer, the COM object may evaporate from under its nose, and the server will be left with a dangling reference, probably causing a crash when further interface method calls are attempted.|
|•||In the case of “out” parameters the server is responsible for AddRef’ing the interface pointer it passes out, and the client is responsible for Release’ing it when it has finished with it. If the client fails to Release the pointer, then the interface will be orphaned and a memory leak will result.|
|•||In the case of “in-out” parameters the client is responsible for all reference count management, except when the server overwrites the parameter, in which case it has to Release the interface pointer passed by the client, and AddRef that it is returning.|
These rules are quite logical and must be applied rigorously to avoid memory leaks and crashes.
In Dolphin one “captures” an interface pointer in one of two ways, depending on whether one needs to add a further reference count (normally the case), or whether instead one needs to “assume” responsibility for a reference that has already been added. To add a further reference count one constructs an instance of the relevant COMInterface class with the #fromAddress: instance creation method. To assume an existing reference one constructs the interface pointer with the alternative instance creation method, #attach:. In either case the reference count will be automatically decremented on finalization of the COMInterface subinstance, so one should not call Release explicitly. It is, however, acceptable to send #free to reduce the finalization overhead when one knows the interface pointer is no longer required.
For all but the simplest of COM servers one is likely to need to return an interface pointer as an “out” parameter. In order to correctly maintain the reference count of such an “out” parameter one must either explicitly AddRef the interface pointer before assigning it to the out parameter, or in many circumstances one can #detach an interface pointer that has been returned by a subsidiary method (in which case one is telling the interface pointer that it is no longer responsible for the reference, which is being assigned elsewhere). An example of this is show in the framework’s generic QueryInterface implementation in the COMObjectStub class (see COMObjectStub>>innerQueryInterface:ppvObject:).
In-out parameters are thankfully quite rare, as they have limited uses in well-designed systems. These are the most complex to manage correctly, but as long as one follows the rule of freeing/releasing the thing one is about to overwrite and allocating/referencing the thing one overwrites it with, all should be well.