This section will help you if you're already familiar with nsCOMPtr
but you need details. If you've never use nsCOMPtr
s before, you might want to read the Getting Started Guide first. If you're trying to fix a broken build, the FAQ might lead you to the answer more quickly.
The Basics
Design
An nsCOMPtr
is designed to be a complete replacement for raw XPCOM interface pointers where they are used as owning references. Almost any place you could use a raw XPCOM interface pointer, you should be able to use an nsCOMPtr
. An nsCOMPtr
is the exact same size and shape as a raw XPCOM interface pointer. It can be used as a member variable without introducing bloat.
Most of the work of being an owning reference can be done in the constructor, destructor, and assignment operators of nsCOMPtr
. Whenever you `point' the nsCOMPtr
at a different XPCOM object (by assignment or initialization), it must Release
its old value, if any, and AddRef
the new. At its own destructor time it must Release
as well. nsCOMPtr
only does exactly the work you would have done, if you always remembered to do the right thing.
Safety Features
Type Safeguards
It is an invariant of nsCOMPtr
that it holds the XPCOM-correct interface pointer for it's underlying type. For example, an nsCOMPtr<nsIFoo>
will always hold the pointer that would be returned by QueryInterface
, when querying an XPCOM object for its nsIFoo
interface. In debug builds, if you subvert this invariant with one of the assignment forms that doesn't call QueryInterface
, nsCOMPtr
will assert at runtime in the bad assignment.
// Given two un-related interfaces |nsIFoo| and |nsIBar|... nsIBar* bar = ...; // ... nsCOMPtr<nsIFoo> foo = bar; // NS_ASSERTION: "QueryInterface needed" // ...even assuming you can get the line to compile // (either by casting, or because the types are related by C ) |
This invariant is relaxed for nsCOMPtr<nsISupports>
. Like nsISupports*
(or even void*
), people generally use nsCOMPtr<nsISupports>
to mean "any XPCOM interface." It would be annoying if nsCOMPtr
forced you to QueryInterface
to the XPCOM-correct nsISupports
within an object in places where you don't care to know the exact type.
NULL
-dereference Safeguards
An nsCOMPtr
will also assert at runtime if you try to dereference it when it is void, e.g.,
nsCOMPtr<nsIFoo> foo; // Note: default initialized to |0| foo->DoSomething(); // NS_PRECONDITION: "You can't dereference a NULL nsCOMPtr with operator->()" |
A similar precondition intervenes on behalf of operator*
.
Reference-Counting Safeguards
All of the operations that extract the underlying raw pointer out of an nsCOMPtr
use a C trick to implement another safety feature. The pointer returned cannot be AddRef
ed, Release
d, or delete
d.
nsCOMPtr<nsIFoo> foo = ...; foo->AddRef(); // Error: |AddRef| is private delete foo.get(); // Error: |operator delete| is private NS_RELEASE(foo); // Error: |Release| is private |
Of course, the most important safety feature provided by nsCOMPtr
is that it AddRef
s and Release
s automatically at the appropriate times.
Casting
Never use old-style C/C++ casts on an nsCOMPtr
. An old-style cast is guaranteed to compile, even if it can't do the right thing. Old-style casts degenerate into the equivalent of reinterpret_cast
if no conversion is defined. Such a cast can easily by-pass nsCOMPtr
s machinery, causing leaks, type mismatches, and other calamities.
// Old-style C/C++ casts by-pass |nsCOMPtr|s machinery and cause leaks... nsresult rv; nsCOMPtr<nsIFoo> foo = ...; // ... rv = GetFoo( (nsIFoo**)&foo ); rv = GetFoo( &(nsIFoo*)foo ); // Sure, they compile; but now you leak. |
To help prevent this, we are trying to make the first form, above, illegal by making operator&
private
. See bug 59414.
Implementation Details and Debugging Machinery
Although it is a class, nsCOMPtr
has no virtual methods, and therefore, no vtable or vptr. Because a few key routines are factored out into a common non-template base class, the actual underlying pointer is stored as an nsISupports*
(except in debug builds where NSCAP_FEATURE_DEBUG_PTR_TYPES
is turned on). It is because of these factored routines that nsCOMPtr
users must link with the XPCOM library.
When NSCAP_FEATURE_DEBUG_PTR_TYPES
is turned on, instead of holding its underlying pointer in a variable of type nsISupports*
, the nsCOMPtr
holds it in a pointer matching the underlying type. This allows source level debuggers to more easily "follow" the pointer. However, the routines that would normally be factored into a base class now must become template-specific inlines. There is no factored base class. This implies that the entire application must be compiled with the same setting of NSCAP_FEATURE_DEBUG_PTR_TYPES
, else some parts will be expecting a base class and others will not. The app will not link.
Unit Tests
The unit tests for nsCOMPtr
can be found in the file
Initialization and Assignment
Built-in forms
Assignment into, or initialization of an nsCOMPtr
is easy to understand. The nsCOMPtr
Release
s its old value, if any, and then assigns in the new value, AddRef
ing it and/or calling QueryInterface
as you direct by "annotating" the assignment with directives like dont_AddRef
. This section describes each of the possibilities, though the directives can be more succinctly described in the table below.
You can construct an nsCOMPtr
from, or assign into it any of the following
- the value
0
- another
nsCOMPtr
of the same type - a raw XPCOM interface pointer of the same type
- a raw XPCOM interface pointer of the same type, annotated with the
dont_QueryInterface
directive - a raw XPCOM interface pointer of the same type, annotated with the
dont_AddRef
directive or a synonym - any interface pointer (either
nsCOMPtr
or a raw XPCOM interface pointer) of any type, annotated with thedo_QueryInterface
directive - a
do_QueryReferent
directive
The first three of these are simple and obvious. The fourth (applying the dont_QueryInterface
directive) is a synonym for just assigning in a raw XPCOM interface pointer of the same type. The remaining directives provide some additional control in special situations. Additionally, you can construct an nsCOMPtr
without supplying an initial value, in which case it is initialized to 0
. Just like a primitive pointer, an nsCOMPtr
with the value 0
points to no object, and can be tested with expressions like if (foo)
and if (!foo)
.
The directives mentioned above may make more sense in this table
don't QI | QI | |
AddRef |
|
|
don't AddRef |
E.g., one of the possibilities for assigning into an nsCOMPtr
, but you don't want to AddRef
the pointer you are assigning (because it has already been AddRef
ed for some reason) is dont_AddRef(T*)
found at the intersection of "don't AddRef
" and "don't QI". Here is a sample demonstrating the various positions these `annotations' can appear in, using dont_AddRef
// Controlling assignment into an |nsCOMPtr|... // in constructors... nsCOMPtr<nsIFoo> foo1( dont_AddRef(rawFoo1Ptr) ); nsCOMPtr<nsIFoo> foo2 = dont_AddRef(rawFoo2Ptr); // Note that the function form (called `direct initialization') and the // assignment form (called `copy initialization') of a constructor have // subtly different meanings; prefer direct initialization. nsCOMPtr<nsIFoo> foo3; // in a normal assignment... foo3 = dont_AddRef(rawFoo3Ptr); // The annotations described in the table apply to both forms of constructor, // and to plain-old assignment |
Any of the annotations shown in the table can appear in all the positions demonstrated with dont_AddRef
. The sections that follow describe each possibility.
nsCOMPtr<T> = T*
,
nsCOMPtr<T> = dont_QueryInterface( T* )
The default behavior, shown in the table as T*
, is to AddRef
the new value, but not to call QueryInterface
against it. This is what happens when no `annotation' is present, e.g.,
nsCOMPtr<nsIFoo> foo( aFooPtr ); // or foo = aFooPtr; // ...will call |AddRef| but not |QueryInterface| // A more explicit synonym for this is... nsCOMPtr<nsIFoo> foo( dont_QueryInterface(aFooPtr) ); // or foo = dont_QueryInterface(aFooPtr); |
By using this form, you are promising that the pointer you are assigning in is already a pointer to the XPCOM-correct interface matching the nsCOMPtr
s underlying type, in this case, nsIFoo
.
nsCOMPtr<T> = do_QueryInterface( nsISupports* )
,
nsCOMPtr<T> = do_QueryInterface( nsISupports*, nsresult* )
If you can't satisfy the above promise, you can `annotate' the assignment to tell the nsCOMPtr
it needs to call QueryInterface
, e.g.,
nsCOMPtr<nsIFoo> foo( do_QueryInterface(aBarPtr) ); // or foo = do_QueryInterface(aBarPtr); // ..._will_ call |QueryInterface| (and therefore, |AddRef|) // Of course, since you're calling |QueryInterface|, you might need the // error result... nsresult rv; nsCOMPtr<nsIFoo> foo( do_QueryInterface(aBarPtr, &rv) ); // or foo = do_QueryInterface(aBarPtr, &rv); |
nsCOMPtr<T> = dont_AddRef( T* )
,
nsCOMPtr<T> = getter_AddRefs( T* )
Sometimes, you happen to have a pointer lying around that's already AddRef
ed, but you want to put it into an nsCOMPtr
. This often happens with getters that return the AddRef
ed pointer as their result (rather than an nsresult
); or in the efficiency transformations. dont_AddRef
is the perfect remedy to situations like this.
nsIFoo* temp; nsresult rv = GetFoo(&temp); nsCOMPtr<nsIFoo> foo( dont_AddRef(temp) ); // |temp| has already been |AddRef|ed, but we want to manage it with an // |nsCOMPtr|. nsCOMPtr<nsIFoo> foo( getter_AddRefs(CreateAFoo()) ); // |getter_AddRefs| is a synonym for |dont_AddRef| that may look better to // you when applied to functions that return |AddRef|ed pointers nsCOMPtr<nsIFoo> foo( dont_AddRef(CreateAFoo()) ); // or, maybe you don't like it better... |
nsCOMPtr<T> =
/* call QueryInterface
but don't AddRef
*/
You'll notice this quadrant of the table is marked "n/a". There is no explicit directive that means "call QueryInterface
, but don't AddRef
the result". This option corresponds to the situation where you are calling a getter that returns an object of the wrong type. It has already AddRef
ed the object, so you don't want to, but you need to get a different interface out of it. Well, you can't have it. QueryInterface
always AddRef
s it's result, and there is no substitute for calling QueryInterface
to get the right type. The solution is a two step process.
// ... // The getter returns an already |AddRef|ed object (of the wrong type)... nsCOMPtr<nsIBar> bar( getter_AddRefs(CreateBar()) ); // ...which I must query for the right type nsCOMPtr<nsIFoo> foo( do_QueryInterface(bar) ); |
One unfortunate trap that people fall into in this case is forgetting that their getter function AddRef
ed the result. Which leads them to type in code that looks like this:
nsCOMPtr<nsIFoo> foo( do_QueryInterface(CreateBar()) ); // Oops! The interface returned by |CreateBar| leaks. // You _must_ handle this case with the two step solution shown above. // Seems unlikely, you say? You're more likely to see it in a form like this nsCOMPtr<nsIFoo> foo( do_QueryInterface(aList->ElementAt(i)) ); // |ElementAt|, like all good getters, |AddRefs| it's result // which would be dropped on the floor, after querying it for the needed // interface |
Bugzilla bug 8221 is specifically about finding and fixing this particular kind of leak.
nsCOMPtr
helpers
nsCOMPtr<T> = do_QueryReferent( nsIWeakReference* )
,
nsCOMPtr<T> = do_QueryReferent( nsIWeakReference*, nsresult* )
do_QueryReferent
exists to facilitate weak references based on nsIWeakReference
. An nsIWeakReference
is an XPCOM object that acts as a proxy for another object. The nsIWeakReference
and this other object have a special relationship. They know about each other, but neither holds an owning reference to the other. The two objects cooperate to ensure that neither ever holds a dangling pointer to the other. Holding an owning reference on the nsIWeakReference
object allows you to get to this other object when you need to, but does not require it to go on living, just for you. To get to the object, you ask the nsIWeakReference
object to QueryInterface
it on your behalf. If the object still exists and supports the requested interface, you will (hopefully, temporarily) hold an owning reference to it.
nsIWeakReference* weakPtr = ...; weakPtr->QueryReferent( |
Using an nsCOMPtr<T>
as a T*
Using an nsCOMPtr
as a pointer
"In" Parameters
"Out" Parameters: getter_AddRefs
Assignment into an nsCOMPtr
is fairly easy to understand. The nsCOMPtr
Release
s its old value, if any, and then assigns in the new value, AddRef
ing, and/or calling QueryInterface
as you specified with the directives described above. These rules apply equally to the "assignment" that happens when copying parameters or function results that are declared to be nsCOMPtr
s. If we want nsCOMPtr
s to be a viable substitute for raw XPCOM interface pointers, however, we will need to deal with the issue of "out" parameters. Many XPCOM functions return interface pointers as results through parameters, e.g.,
// Getters can return interface pointers through "out" parameters... nsresult GetFoo( nsIFoo** ); // a standard getter nsresult GetFoo2( nsIFoo*& ); // a non-standard getter nsresult GetSomething( void** ); // an "un-typed" getter // Note: |QueryInterface| is an example of a "un-typed" getter |
We must be able to pass nsCOMPtr
s by pointer or reference, into routines for use as "out" parameters. The problem is that inside the getter there is no knowledge of nsCOMPtr
s. It thinks it's getting a pointer (or a reference) to a raw XPCOM interface pointer. nsCOMPtr
s smart assignment operators will not be called. The old value, if any, will be leaked.
This is where the getter_AddRefs( nsCOMPtr& )
comes in. getter_AddRefs
Release
s the old value, if any, clears it out, and returns a pointer to it, allowing the getter to fill in your nsCOMPtr
with a new AddRef
ed value. We use getter_AddRefs
as a sort of replacement for the &
that we would apply to a raw XPCOM interface pointer in these situations. getter_AddRefs
packages up all the magic we normally get from nsCOMPtr
s constructors and assignment operators.
// raw XPCOM interface pointers... nsIFoo* foo; GetFoo(&foo); GetFoo2(foo); GetSomething((void**)&foo); |
// |nsCOMPtr|s... nsCOMPtr<nsIFoo> foo; GetFoo(getter_AddRefs(foo)); GetFoo2(*getter_AddRefs(foo)); GetSomething(getter_AddRefs(foo)); |
Why not just overload operator&
to do this work? Several reasons: it would become inconvenient take the address of an nsCOMPtr
in all other situations; the name "getter_AddRefs
" enforces the notion that a certain behavior is required of the getter; and once upon a time, there was another possibility (as you're about to learn).
Is there a getter_doesnt_AddRef( nsCOMPtr& )
for getters that return non-AddRef
ed results through a parameter? No, there isn't. Once upon a time, there was, but it went away for three reasons:
- It is against the rules of XPCOM for a getter to return a non-
AddRef
ed interface pointer through a parameter (if you see it, report a bug). getter_doesnt_AddRef
had complex ramifications that ended up makingnsCOMPtr
s either bigger or slower than raw XPCOM interface pointers.- You can still call such a getter and put the result into an
nsCOMPtr
with a temporary, e.g.,
// Calling a getter that (illegally) doesn't |AddRef| its result... nsIFoo* temp; nsresult rv = GetFoo_WithoutAddRef(&temp); // Note to self: must report |GetFoo_WithoutAddRef| as a bug, all getters // must |AddRef| nsCOMPtr<nsIFoo> foo = temp; |
"In/Out" Parameters
What about "in/out" parameters?
Efficiency and Correctness
The Costs of nsCOMPtr
nsCOMPtr
is tuned to be a viable replacement for raw XPCOM interface pointers, anywhere you would use one as an owning reference. nsCOMPtr
s performance is generally slightly more efficient that raw pointers in space, and negligably less efficient in time. Performance concerns should not deter you from using nsCOMPtr
. The patterns presented throughout this section will help you get the most out of nsCOMPtr
.
Space
In general, nsCOMPtr
can be more efficient in space than using raw XPCOM pointers. This is primarily because it factors its destructor, and the more complicated constructors and assignment operators. By following the optimization tips in this section, you will write code that generates fewer bytes of object than you might with raw pointers. Even if you don't follow these suggestions, your nsCOMPtr
code may still end up smaller, or at worst only negligibly bulkier than the raw pointer version. See Code Bloat [LONG, summary at top] for details, though the recommendations from that document are re-iterated here.
Time
[[More time-performance measurements are needed.]]
In places where two or more subroutines calls are required, i.e., of AddRef
, Release
, and QueryInterface
, some nsCOMPtr
routines are factored, and hence, require additional time corresponding to invoking a subroutine. This time is negligable, especially in the face of work done by QueryInterface
, and the work that may be done by Release
.
In all other cases, nsCOMPtr
does only the work you would have done by hand. The bulk of the work for which an nsCOMPtr
is used is dereferencing with operator->
, just as it is with a primitive pointer. On every platform, this operation generates the exact same code, and takes the same time, as performing this operation on a raw XPCOM interface pointer. The destructor, which corresponds to client code calling Release
against a raw XPCOM interface pointer, is factored, requiring the extra time required to invoke a subroutine call, though this is balanced against the cost already present in both cases of calling Release
which may, in turn, invoke delete
and the referents destructor. All nsCOMPtr
s constructors and assignment operators are inline. The simple constructors, i.e., those that don't query, do only exactly the same work that you would do by hand. Any routines that call more than one of AddRef
, Release
, or QueryInterface
, are factored, and hence have the additional cost of invoking a subroutine call.
Only the fact that some routines are factored, thus introducing the overhead of an additional subroutine call, and that initialization cannot be by-passed, cause any extra run-time cost for nsCOMPtr
over raw XPCOM interface pointers. Space and time trade-offs are finely balanced in nsCOMPtr
. The factored routines are the direct result of bloat measurements.
Prefer Construction to Assignment
The most efficient way, in both time and space, to get a value into an nsCOMPtr
is at construction time. Prefer construction over assignment whenever reasonable. Initialize member nsCOMPtr
s in the member initialization clause of your constructor.
// Initialize member |nsCOMPtr|s in the member initialization clause of your // constructor... class Bar { public: Bar( nsIFoo* initial_fooPtr ); // ... private: nsCOMPtr<nsIFoo> mFooPtr; }; Bar::Bar( nsIFoo* initial_fooPtr ) : mFooPtr(initial_fooPtr) // initialize it _here_ { // not here } |
Additionally, there is an optimization pattern using a temporary that converts assignment form to construction form.
// Default construction, followed by // assignment is not as efficient... nsCOMPtr<nsIFoo> foo; nsresult rv=GetFoo(getter_AddRefs(foo)); |
// ...as construction alone. nsIFoo* temp; nsresult rv=GetFoo(&temp); nsCOMPtr<nsIFoo> foo=dont_AddRef(temp); // Remember this `raw-pointer, call // getter, assign |dont_AddRef|' // pattern. It crops up in many // efficiency discussions. |
In both cases you end up with foo
, a valid nsCOMPtr
whose value was set with the product of GetFoo
, and rv
the status returned by GetFoo
. The case using the temporary, however, uses construction to put the value into the nsCOMPtr
, which (though slightly more complicated in source) is more efficient than default construction followed by assignment, the course of events followed by the simpler example.
Prefer Destruction to Assignment
Prefer do_QueryInterface
to calling QueryInterface
Iterating
There is a very common idiom for iterating over data-structures with normal pointers, e.g.,
// Iterating with pointers to non-XPCOM objects... Node* p = ...; while ( p ) { // ... p = p->next; } |
One often sees this pattern expressed as a for
loop, as well. Consider, however, what would happen if you were trying to do this with a raw XPCOM interface pointer.
// Iterating with raw XPCOM interface pointers... nsIDOMNode* p = ...; while ( p ) { // ... p->GetNext(&p); // Trouble! We overwrote |p| without |Release|ing it. } |
Oops! We just failed to Release
p
before putting a new pointer into it. People do this a lot, and it turns out to be a big source of leaks in normal XPCOM code. Well, could we do this instead?
// Iterating with raw XPCOM interface pointers... nsIDOMNode* p = ...; while ( p ) { // ... NS_RELEASE(p); p->GetNext(&p); // Trouble! We tried to call a member function of a pointer // that may be dangling or |NULL|. } |
Unfortunately, not. After the Release
, p
could be dangling. In fact, if you used the NS_RELEASE
macro, p
would be NULL
by the time you got to the GetNext
call.
Now imagine that you've written the same thing with nsCOMPtr
.
// Iterating with |nsCOMPtr|s... nsCOMPtr<nsIDOMNode> p = ...; while ( p ) { // ... p->GetNext( getter_AddRefs(p) ); // Trouble! We tried to call a member function through a |NULL| pointer. } |
Using nsCOMPtr
is exactly like using raw XPCOM interface pointers, here. getter_AddRefs
Release
s and clears out p
before you assign into it, i.e., before GetNext
is called. Which means that by the time we get around to calling GetNext
, we are trying to call it through a NULL
pointer. Unlike raw XPCOM interface pointers, nsCOMPtr
will fire an assert
instead of blindly trying to call GetNext
through a NULL
pointer.
That's the problem. So what's the solution? If this were raw XPCOM interface pointers, we'd probably introduce a temporary. We can do the same thing with nsCOMPtr
.
// Safe iterating with raw XPCOM // interface pointers... nsIDOMNode* p = ...; while ( p ) { // ... // Introduce a temporary so we // don't stomp on |p| nsIDOMNode* temp = p; temp->GetNext(&p); NS_RELEASE(temp); } |
// Safe iterating with |nsCOMPtr|... nsCOMPtr<nsIDOMNode> p = ...; while ( p ) { // ... // Introduce a temporary so we // don't stomp on |p| nsCOMPtr<nsIDOMNode> temp = p; temp->GetNext(getter_AddRefs(p)); } |
Although the nsCOMPtr
parallel is easy to understand, it suffers from doing one extra AddRef
and one extra Release
compared to the raw pointer scheme. A slight transformation makes the code uglier, but (possibly negligibly) more efficient.
// Safe, efficient, iterating with |nsCOMPtr|... nsCOMPtr<nsIDOMNode> p = ...; while ( p ) { // ... nsIDOMNode* next; p->GetNext(&next); p = dont_AddRef(next); } // Look! It's our friend, the `raw pointer, call getter, assign // |dont_AddRef|' pattern. |