1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
---
title: Weak reference
slug: Mozilla/Tech/XPCOM/Weak_reference
tags:
- XPCOM
translation_of: Mozilla/Tech/XPCOM/Weak_reference
---
<p>In <a href="ja/XPCOM">XPCOM</a>, a <b>weak reference</b> is a special object that contains a pointer to an XPCOM object, but does <i>not</i> keep that object alive. If the referent object is destroyed before the weak reference, the pointer inside the weak reference is set to <code>nsnull</code>.
</p>
<h3 id="なぜこれを使う必要があるのでしょうか?"> なぜこれを使う必要があるのでしょうか? </h3>
<p>When you hold an owning reference on an object (i.e., you have <code>AddRef</code>ed it), you are holding that object in existence for the duration of your reference. This isn't always appropriate, and can lead to trouble. If, for instance, this owning reference is part of a cycle of owning references (e.g., if the referenced object also holds a owning reference back to you), then none of the objects in the cycle can be reclaimed without taking special measures.
</p><p>There are less severe situations. A naive design for an observer/observable relationship would require the observable to hold a owning reference to the observer. After all, the observable must send messages to each observer, notifying it of the appropriate state changes. To do that, it will call a method on the observer, so it needs a pointer. And the call would fail if the pointer were allowed to dangle, therefore, the pointer should be a owning reference. This design, however, is flawed, as it holds the observer in existence longer than would otherwise be necessary. The observer may only need a short life compared to the thing being observed. It might go away after the first interesting event, even. But in this design, by hitching its life to the observable, it is kept on life-support long past any need or use.
</p><p>What is actually needed in this case, is either out-of-band signaling, where when the observer wants to go away, it unregisters itself from the observable, which then releases its owning reference, allowing the observer to die, or else a new kind of reference. This document describes an implementation of <i>weak references</i>. A weak reference does not hold its referent in existence, but also will not dangle. When the referent is destroyed, the weak reference automatically becomes <code>nsnull</code>. This technique can significantly simplify certain relationships, and you should consider it when an owning reference is inappropriate, but where a raw pointer might end up dangling.
</p>
<h3 id="どのように使うのでしょうか?"> どのように使うのでしょうか? </h3>
<p>サンプルをここに挙げます。新しい所と興味を引きそうな所は、強調して表現します。
</p>
<pre class="eval"><strong>#include "nsWeakPtr.h"</strong>
// ...
// 弱い参照を得るのは、簡単です...
<strong>nsWeakPtr</strong> weakPtr = getter_AddRefs( <strong>NS_GetWeakReference(</strong>aFooPtr<strong>)</strong> );
// ...
{ // ...しかし、弱い参照を使うには、(短命の) 所有する参照を必要とします。
nsCOMPtr<nsIFoo> tempFooPtr = <strong>do_QueryReferent</strong>(weakPtr);
if ( tempFooPtr )
tempFooPtr->SomeFooMethod(...);
// そうでないと、「本当の」オブジェクトが無くなってしまいます。
}
</pre>
<p>しかし、実際の例では、弱い参照はメンバー変数として保持される場合が多いでしょう。以下の例では、<code>nsObservable</code> は、イベントを報告するために、それぞれのオブザーバーへの参照を保持する必要があります。しかし、<code>nsObservable</code> は、ダングリングポインターを防ぐためだけにオブザーバーを保持するのは、望ましくありません。そのため、<code><a href="ja/NsIObserver">nsIObserver</a></code> への所有する参照を保持する代わりに、弱い参照を保持します。弱い参照は、人為的にオブザーバーの生存期間を伸ばしたりしません。その上、ダングリングすることもしないのです。
</p><p>以下では、渡されてくる <code>nsIObserver</code> が <code>nsISupportsWeakReference</code> も実装していると仮定しています。一つのオブザーバーの管理の仕方を参考にすれば、オブザーバーのリストの管理の仕方について推測することはできるでしょう。
</p>
<pre class="eval">class nsObservable
{
public:
// ...
nsresult AddObserver( nsIObserver* );
nsresult NotifyObservers( nsIMessage* );
// ...
private:
nsWeakPtr mObserver;
// ...あるいはここにオブザーバーのリストがあると想像してください
};
// ...
nsresult
nsObservable::AddObserver( nsIObserver* aObserver )
{
mObserver = getter_AddRefs( NS_GetWeakReference(aObserver) );
// ...あるいはこれをオブザーバーのリストに追加してください
return NS_OK;
}
nsresult
nsObservable::NotifyObservers( nsIMessage* aMessage )
{
nsCOMPtr<nsIObserver> observer = do_QueryReferent(mObserver);
if ( observer )
observer->NoticeMessage(aMessage);
else
mObserver = 0;
// あるいはこのオブザーバーをリストから取り除くことで、それはなくなってしまうでしょう
return NS_OK;
}
// ...
</pre>
<p><code>nsWeakPtr</code> が <code>nsCOMPtr</code> とちょうど同じインタフェースを持つことがキーになります。実は、<code>nsWeakPtr</code> はこのように定義されています。
</p>
<pre class="eval">typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
</pre>
<h3 id="こりゃ最低だ!"> こりゃ最低だ! </h3>
<p>この弱い参照の実装があなたが望むインタフェースを提供しないことに、おそらく今までに気づいたでしょう。
</p>
<h4 id="なぜ、弱い参照の上の私のインタフェースメソッドを直接呼べないのでしょうか?"> なぜ、弱い参照の上の私のインタフェースメソッドを直接呼べないのでしょうか? </h4>
<p>あなたは、この弱い参照の仕組みを使って、あなたが実際に使いたいインタフェースを実装するポインターを得られることを望んでいるでしょう。例えば、
</p>
<pre class="eval">// 注: 実際にはこのような実装ではありません
nsWeakPtr<nsIFoo> weakFooPtr = fooPtr;
// ...
if ( weakFooPtr )
status = weakFooPtr->SomeFooMethod(...);
</pre>
<p>これは、合理的な要求です。しかし、自動的に実装するのは、とても高くつくのです。継承でも、テンプレートでも、マクロでも自動的にすべてのメソッド呼び出しを実際のオブジェクトへ転送するのを支援してくれません。XPIDL は、(もし修正すれば) そのような実装を作成できるかもしれません。そうでなければ、上で議論したようなことを手で書くことができます。極わずかですが、その他のコストもあります。それは、間接呼び出しにより、呼び出しごとにかかる余分なコストです。そして単純な実装では、対象実装へのインタフェースごとに余分なポインターを追加します。
</p>
<h4 id="なぜペアの間で単に_QueryInterface_できないのでしょうか?"> なぜペアの間で単に <code>QueryInterface</code> できないのでしょうか? </h4>
<p>あなたが保持している <code><a href="ja/NsIWeakReference">nsIWeakReference</a></code> は、本当に単に対象のオブジェクト上のインタフェースのように感じられるでしょう。二つの間で単に <code>QueryInterface</code> を実行したいのは、合理的なことです。なぜ余分な呼び出し <code>GetWeakReference</code> と <code>QueryReferent</code> が必要なのでしょうか ? 弱い参照が実際に対象のオブジェクトに集約されていれば可能なように思えます。
</p><p>ここでの問題は、<code>QueryInterface</code> です。<code>QueryInterface</code> は、COM が動作するのに必要な多くのことを満たす必要があります。これらの要求の元では、同じ (集約する) オブジェクトに対して同じインタフェースの QueryInterface を呼び出した場合は、同じ結果にならなければなりません。これは、どのインタフェースポインターを通じて呼び出そうとも、いつ呼び出そうとも要求されることです。我々の状況では、これは不可能です。なぜなら、それは、明らかに集約の部分を破壊可能であるという事実があるためです。後続の処理で、その部分へ到着しようとしても <code>nsnull</code> が返ります。時々、弱いポインターを通じた <code>QueryInterface</code> が「本当の」インタフェースを返すでしょう。そして時々 <code>nsnull</code> を返すのです。それは、単に COM を壊すことになります。
</p><p>このように、弱い参照が対象オブジェクトへと集約されないのは明らかです。それため、それらの間を動くのに <code>QueryInterface</code> を使うことはできません。これは、より便利に感じられると思いますが、グローバルルーチンの <code>NS_GetWeakReference</code> を使うと、対象のオブジェクトから弱い参照を得ることができます。そして、<code>nsIWeakReference::QueryReferent</code> が、逆の方向に対して <code>QueryInterface</code> と同じ機能を提供します。付け加えると、現在、<code>nsCOMPtr</code> は、新しいキーの <code>do_QueryReferent</code> をサポートするようになり、人生をいくぶんシンプルにしてくれています。
</p>
<h3 id="クラスが弱い参照をサポートするようにするにはどうすればよいでしょうか?"> クラスが弱い参照をサポートするようにするにはどうすればよいでしょうか? </h3>
<p>単に <code><a href="ja/NsSupportsWeakReference">nsSupportsWeakReference</a></code> を継承するだけです。ミックスインクラスがすべての作業を行ってくれます。そして、あなたの <code>QueryInterface</code> を適宜変えてください。例えば、
</p>
<pre class="eval">//...
<strong>#include "nsWeakReference.h"</strong>
class nsFoo : public nsIFoo, ...<strong>, public nsSupportsWeakReference</strong> { ... };
// ...if you used the table macros to implement |QueryInterface|, add an entry
NS_INTERFACE_MAP_BEGIN(nsFoo)
// ...
<strong>NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)</strong>
// ...
NS_INTERFACE_MAP_END
// ...if you used a |NS_IMPLE_QUERYINTERFACEn| macro, move up to the next higher one and add a term
NS_IMPL_QUERYINTERFACE<strong>n+1</strong>( ...<strong>,nsISupportsWeakReference</strong>)
// ...if you implemented |QueryInterface| by hand, add a clause
NS_IMETHODIMP
nsFoo::QueryInterface( REFNSIID aIID, void** aInstancePtr )
{
// ...
<strong>else if ( aIID.Equals(nsCOMTypeInfo<nsISupportsWeakReference>::GetIID()) )
*aInstancePr = NS_STATIC_CAST(nsISupportsWeakReference*, this);</strong>
// ...
}
</pre>
<h3 id="代替手段"> 代替手段 </h3>
<p>This technique is useful, but in situations where you need this, there are two alternatives which you may want to consider:
</p>
<ul><li> You might hold an owning reference, but arrange to <code>Release</code> it out-of-band; this must be before the destructor, which would otherwise never be called.
</li><li> You might hold a raw pointer (without <code>AddRef</code>ing and <code>Release</code>ing it), and avoid using it in cases where it might dangle.
</li></ul>
<h3 id="参考資料"> 参考資料 </h3>
<ul><li> The source
<ul><li> <code><a href="https://dxr.mozilla.org/mozilla-central/source/xpcom/base/nsIWeakReference.idl" rel="custom">xpcom/base/nsIWeakReference.idl</a></code>
</li><li> <code><a href="https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsWeakReference.h" rel="custom">xpcom/glue/nsWeakReference.h</a></code>
</li><li> <code><a href="https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsWeakReference.cpp" rel="custom">xpcom/glue/nsWeakReference.cpp</a></code>
</li></ul>
</li><li> <a href="ja/XPCOM_ownership_guidelines">XPCOM ownership guidelines</a>
</li><li> <a href="ja/Using_nsCOMPtr">Using nsCOMPtr</a>
</li></ul>
<div class="originaldocinfo">
<h2 id="原文書の情報"> 原文書の情報 </h2>
<ul><li> 著者: <a class="external" href="http://ScottCollins.net/">Scott Collins</a>
</li><li> 最終更新日: September 23, 2000
</li><li> 著作権: Portions of this content are © 1998–2007 by individual mozilla.org contributors; content available under a Creative Commons license | <a class="external" href="http://www.mozilla.org/foundation/licensing/website-content.html">詳細</a>
</li></ul>
</div>
<div class="noinclude">
</div>
|