Reference Manual 编辑
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.
|
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.,
|
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.
|
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.
|
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
|
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.,
|
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<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.
|
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.
|
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:
|
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.
|
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.,
|
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.
|
|
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.,
|
"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.
|
Additionally, there is an optimization pattern using a temporary that converts assignment form to construction form.
|
|
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.,
|
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.
|
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?
|
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
.
|
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
.
|
|
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.
|
Writing Getters
Compiler Annoyances
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论