Reference Manual 编辑

This section will help you if you're already familiar with nsCOMPtr but you need details. If you've never use nsCOMPtrs 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 AddRefed, Released, or deleted.

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 AddRefs and Releases 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 nsCOMPtrs 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 Releases its old value, if any, and then assigns in the new value, AddRefing 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 the do_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

Table 1. Options for assigning into an nsCOMPtr.
 don't QIQI
AddRef

T*,
dont_QueryInterface(T*)

do_QueryInterface(nsISupports*),
do_QueryInterface(nsISupports*, nsresult*)
do_QueryReferent(nsIWeakReference*),
do_QueryReferent(nsIWeakReference*, nsresult*)

don't AddRef

dont_AddRef(T*),
getter_AddRefs(T*)

n/a

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 AddRefed 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 nsCOMPtrs 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 AddRefed, but you want to put it into an nsCOMPtr. This often happens with getters that return the AddRefed 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 AddRefed 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 AddRefs 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 AddRefed 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 Releases its old value, if any, and then assigns in the new value, AddRefing, 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 nsCOMPtrs. If we want nsCOMPtrs 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 nsCOMPtrs by pointer or reference, into routines for use as "out" parameters. The problem is that inside the getter there is no knowledge of nsCOMPtrs. It thinks it's getting a pointer (or a reference) to a raw XPCOM interface pointer. nsCOMPtrs 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 Releases 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 AddRefed 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 nsCOMPtrs 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-AddRefed 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-AddRefed interface pointer through a parameter (if you see it, report a bug).
  • getter_doesnt_AddRef had complex ramifications that ended up making nsCOMPtrs 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. nsCOMPtrs 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 nsCOMPtrs 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 nsCOMPtrs 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 Releases 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.

Writing Getters

Compiler Annoyances

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

词条统计

浏览:85 次

字数:37217

最后编辑:7年前

编辑次数:0 次

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文