Rules for Managing Reference Counts
The elegance of using reference counting for object lifetime management is
that independent objects can obtain and release access to a single object, and not
have to coordinate with each other over lifetime management. In a sense, the
object provides this management, so long as the client objects conform to the
rules of use. Within a single object that is completely under the control of a
single development organization, that organization can adopt whatever strategy it
chooses. The following rules are about how to manage and communicate interface
instances between objects, and are a reasonable starting point for a policy
within an object.
The conceptual model is that interface pointers are thought of as living in
pointer variables, which, for this discussion, include variables in memory
locations and in internal processor registers, and both programmer- and
compiler-generated variables. In short, it includes all internal computation state that
holds an interface pointer. Assignment to or initialization of a pointer variable
involves creating a new copy
of an already existing pointer. Where there was one copy of the pointer in
some variable (the value used in the assignment/initialization), there are now
two. An assignment to a pointer variable destroys
the pointer copy presently in the variable, as does the destruction of the
variable itself (that is, the scope in which the variable is found, such as the
stack frame, is destroyed).
must be called for every new copy of an interface pointer, and Release
called for every destruction of an interface pointer except where subsequent
rules explicitly permit otherwise.
This is the default case. Unless special knowledge permits otherwise, the
worst case must be assumed. The exceptions to Rule 1 all involve knowledge of the
relationships of the lifetimes of two or more copies of an interface pointer. In
general, they fall into two categories: 1) nested lifetimes and 2) overlapping
In a nested lifetime relationship, the first copy of the interface pointer is
created, followed by the creation of the second copy. The second copy is
destroyed before the end of the first copy's lifetime. The calls to AddRef
for the second copy can be omitted. The overlapping lifetime relationship
begins like the nested lifetime relationship
the second interface pointer is created after the first. In overlapping
lifetimes, however, the first pointer copy is destroyed before the second pointer
copy. The calls to AddRef
for the second copy and Release
for the first copy can be omitted.
If the piece of code has special knowledge of the relationships of the
beginnings and the endings of the lifetimes of two or more copies of an interface
pointer, it allows you to omit AddRef
pairs. The following rules call out specific common cases of Rule 2. The first
two of these rules are particularly important, as they are especially common.
In-parameters to functions
The copy of an interface pointer which is passed as an actual parameter to a
function has a lifetime which is nested in that of the pointer used to
initialize the value. The actual parameter, therefore, need not be separately reference
Out-parameters from functions, including return values
This situation falls under Rule 2. To set the out parameter, the function
itself, by Rule 1, must have a stable copy of the interface pointer. On exit, the
responsibility for releasing the pointer is transferred from the one called to
the caller. Thus, the out-parameter need not be separately reference counted.
A method implementation clearly has omniscient knowledge of the lifetimes of
each of the pointer variables allocated on the stack frame. It can, therefore,
use this knowledge to omit redundant AddRef
Some data structures are contain two components, A and B, each with a pointer
to the other. If the lifetime of one component (A) is known to contain the
lifetime of the other (B), then the pointer from the second component back to the
first (from B to A) need not be reference counted. Often, avoiding the cycle
that would otherwise be created is important in maintaining the appropriate
deallocation behavior. However, such non-reference counted pointers should be used with extreme caution.
In particular, as the remoting infrastructure cannot know about the semantic
relationship in use here, such backpointers cannot be remote references. In
almost all cases, an alternative design of having the backpointer refer a second
"friend" object of the first, rather than the object itself (thus avoiding the
circularity), is a superior design.
The following rules call out common situations that follow from Rule 1.
In-Out parameters to functions
The caller must AddRef
the actual parameter, since it will be released (with a call to Release
) by the one called when the out-value is stored on top of it.
Fetching a global variable
The local copy of the interface pointer fetched from an existing copy of the
pointer in a global variable must be independently reference counted since
called functions might destroy the copy in the global while the local copy is still
New pointers synthesized out of "thin air."
A function that synthesizes an interface pointer using special internal
knowledge rather than obtaining it from some other source must do an initial AddRef
on the newly synthesized pointer. Important examples of such routines include
instance creation routines, implementations of IUnknown::QueryInterface
Returning a copy of an internally stored pointer
Once the pointer has been returned, the one called has no idea how its
lifetime relates to that of the internally stored copy of the pointer. Thus, the one
called must call AddRef
on the pointer copy before returning it.
Finally, when implementing or using reference counted objects, a technique
called artificial reference counts
sometimes proves useful. In implementing a method of an interface, if you
invoke functions that have even the remotest chance of decrementing your reference
count, this may cause you to release before it returns to the method when it
is called. The subsequent code in the method would crash.
A robust way to protect yourself from this is to insert an AddRef
at the beginning of the method implementation which is paired with a Release
just before the method returns.
These "artificial" reference counts guarantee object stability while
processing is done.
- Software for developers
Software for Android Developers
- More information resources
Unix Manual Pages