【WIP】弱参照(docs/weak.rst) 翻訳
出典
https://github.com/apple/swift/blob/3cbabfd196d3be8d01e97cacef71f41e9c594f1d/docs/weak.rst
弱参照
要約:本書では、他の言語での様々な設計を含む弱参照の一般的な概念について説明し、標準ライブラリで利用できるいくつかの新しいコア言語機能とより洗練されたランタイムサポート機能を提案する
警告
このドキュメントはSwift1.0の計画に使用された。最新の状態に保たれておらず、Swiftの現在、または計画されている動作を記述していない
参照グラフ
基本的な用語を解説する
プログラムメモリは
- ノード
- オブジェクト
- (ローカルスコープのような一時的な割り当てをオブジェクトとして扱う)
- エッジ
- 参照
- (必ずしも接続されていない)
上記のような有向グラフとして抽象的に見ることができる
(もちろん、マルチエッジとセルフエッジは許可されている)
これらのエッジのそれぞれにあるレベルの強度を割り当てることができる
最高レベルのstrong
、それ以外はweak
を呼び出す
全てのオブジェクトは、参照グラフから生じる参照の強さを持つ
これは任意のレベルの強さ、または参照されない特別な値にすることができる
パスの強さはパス内のエッジの最小強度である
パスセットの強さは、セットが空でない場合をのぞいて、全てのパスの最大強度である
この場合、パスは参照されない
一般に、実装はオブジェクトが強参照されている場合には、オブジェクトの割り当てを解除することのみが禁じられている
しかし、何からの形で弱参照されていると、ある種の追加保証が発生する可能性がある
言語の前提条件のセクションを参照して欲しい
サイクルコレクタ環境では、特定のノードにルートとして特別な処理が与えられる
これらのノードは常に強参照される
それ以外の場合、オブジェクトの参照強度はルートからオブジェクトまでの全てのパスの強度になる
非サイクルコレクタ環境では、オブジェクトの参照の強さは、そのオブジェクトの全ての直接参照の強度であり、length=1
のパスとみなされる
この環境配慮は言語の保証となることに注意
例え実装がオブジェクト単独のみ参照されることを自明に証明できるとしても、オブジェクトの割り当てを解除することはできない
特定の種類の参照が完全な保証を受けないことは一般的である
例えば、ローカル変数からの強参照は、変数がもはや必要でなくなった
(ただし、正式にスコープを離れる前に)有効性を失う可能性がある
これはGC環境に広がっている、例えばObjective-CでのARC
いくつかのプログラム、特にUIやサーバーのようなイベントドリブンなプログラムでは、概念上の定常状態の間にキャプチャされた静的参照グラフを考慮すると便利
ローカルスコープは静的グラフ内のオブジェクトへの多数の参照を形成するかもしれないが、スコープが無期限に持続する、または大規模な再構築がトリガされない限り、これらの参照は参照グラフに影響を与えないので、参照強度に意味的な重要性はない
言語とライブラリの前例
ここでは既に管理された先例についてのみ議論するつもりである
オブジェクト参照に特別な振る舞いを与えることなく弱参照の種類を作成することは可能である
オブジェクトが割り当てを解除された場合、参照がぶら下がり、その参照がクラッシュしたり、その原因を引き起こす可能性がある
これは例えば(ARCのような環境で)特定の変数の参照カウント操作を挿入したり、(正確なGC環境で)型マップ内のポインタとして変数のframe offset
をマッピングしないようにした
このぶら下がりをweak
として呼び出すだろう
Objective-C
Objective-Cのすべてのモードは、synthesizeされたプロパティのメモリ管理を自動化する
GCとARCでは、アクセサは、基本的なivar(もちろんcopy/atomicな保証)のための通常のセマンティクスを使用する
MRCでのアクセサは保持またはコピーされていない限り、弱いセマンティクスを使用する、この場合指示対象に対して+1の参照カウントが維持される
GCとARCでは、__weak
で修飾された変数は、参照されたオブジェクトが解放を開始すると直ちにゼロになる
使用上の構文上の違いはない
読み込まれた値が最後に書き込まれたものではなくnilになる可能性がある
読み込みコードがクラッシュする(例えば、ivarアクセス)か、または何もしない(メッセージ送信など)
__weakなivarsからロードされた値の特定の用途については、ARCにオプトイン警告がある
GCでは、
- スキャンされていないヒープメモリ
- オブジェクトポインタ型ではなく、
__strong
または__weak
で修飾されていないインスタンス変数にオブジェクトポインタを格納する
上記によって、ダングリング弱参照を構築することもできる
それ以外の場合、オブジェクトは強参照となる
(ローカルスコープからのすべての参照を含む)
ARCでは、__unsafe_unretained
修飾子を使用するか、Cポインタ型にポインタ値を橋渡しすることで、ダングリング弱参照を構築することができる
C++
C++ではスマートポインタ(e.g. std::unique_ptr
)は通常、ポインタを通常のCポインタとして取得するアクセサを提供することによって弱参照の作成を可能にする(get()
を持っていなくても手動でoperator->
を呼び出し同じ効果を得ることができる
C++のstd::shared_ptr
は共有ポインタからweakポインタ(std::weak_ptr
)を形成することを許可する
weakポインタを直接使用することはできない
(参照元が割り当て解除されている場合はnullポインタを生成する)操作を行うか、参照対象が既に存在する場合に例外をスローするweak_ptr
を使用してshared_ptr
を直接構築することによって、shared_ptr
に最初に変換する必要がある(割り当てが解除される)
weak_ptr
がまだ有効かどうかを明示的に問い合わせる方法もある、これはキャストの結果を調べるよりも効率的である
Java
Javaは弱参照をダングリングするための機能を提供していない
標準ライブラリは(java.lang.ref
の)3つのレベルの弱参照を提供する
参照を再配置することはできない(明示的にクリアすることはできるが)
ユーザは値にアクセスする為にget()
を呼び出す必要があり、nullを返される可能性がある
これらの参照クラスの興味深い議論は、ここでたくさん行われている
Java ReferenceオブジェクトはオプションのReferenceQueueで構築できる
その場合オブジェクトの到達可能性が変更されると、参照オブジェクトがそのキューに追加される
これにより、構造全体を定期的にスキャンしたり、完全にlazy
することなく、クリアされたsoft references
の後にデータ構造をクリーンアップすることができる
サブクラス化することによって参照オブジェクトに追加データを追加することができる
リファレンスは強度の順に表示される
SoftReference
はVMがメモリ不足になるまでオブジェクトに保持される一種のquasi-strong
参照である
softly-referencedオブジェクトのソフト参照はVMがOutOfMemoryErrorをスローする前にクリアされていることが保証されている
参照は参照キューに追加される前にクリアされる(参照キューはオブジェクトを復活できない)
ソフトリファレンスの目的はmemory-sensitiveなキャッシュを有効にすることだが実際にはmemory-sensitiveなキャッシュはおそらく、「メモリが不足するとすぐに落とす」よりも巧妙な置換戦略を実装したいと考えている
より興味深いのはメモリでガイドされるcircuit-breaker
である、非常に大きな構築をする場合はソフト参照で保持して欲しい
構築中に参照がnullになるとただ破棄される、しかしこれはかなり上手くやってもらえるユースケースである
WeakReference
は一意のキャッシュのようなメモリーに依存しない弱いキャッシュでの使用を意図している
指示対象がより強く参照されている間だけ持続する、参照は参照キューに追加される前にクリアされる (参照キューはオブジェクトを復活できない)
PhantomReference
は実際にファイナライザ(オブジェクトを復活させる機能を含むいくつかの問題がある)を実際に使用することなく、オブジェクトに余分なファイナライズを加える方法を提供する
ファントム参照は常にその値としてnullを提示するため、参照としては無意味である
ファントム参照は、オブジェクトがファイナライズされた後にエンキューされる
したがって、仮想マシン内のオブジェクトへの参照がまったく存在しない場合がある
しかし、ファントム参照がすべてクリアされるまでオブジェクト自体は割り当てを解除することができない、
またはそれ自体が指示対象にダングリング弱参照を保持することができる
(又はその直接の参照を読み取ることができるかもしれないネイティブコードの便宜のためであると考えている割り当てを解除する)
.NET
.NETのWeakReference
クラスはJavaのWeakReferenceクラスに似ているが、値に直接アクセスすることはできない
Targetプロパティを使用してアクセスする必要がある
これはnullを返す可能性がある
参照値は別の値に再設定することができる
Target
オブジェクトがファイナライズされるが、実際には割り当てが解除されないようにする弱参照が長く作成されることがある
Python
weakref
は関数オブジェクトのように動作する
特定の値で作成され、再配置することはできない
参照先が収集されている場合、この関数はNoneを返す
弱い参照として値を自動的にプロキシするライブラリ機能がある
プロキシで操作が実行されても参照先が収集されている場合は例外が投げられる
weakref
はコールバック関数で構築することができる
コールバックは弱参照がクリアされた後に呼び出される
しかしそれは弱いrefオブジェクト自体を渡される
Ruby
WeakRef
は自動的にオブジェクトのプロキシである
参照がまだいきているかどうかを調べるweakref_alive
メソッドがある
別の操作で例外が投げられる
Rust
筆者が知っている限り、現時点でRustの弱参照のようなものはない
TBD
A managed pointer (@int) is a strong reference subject to GC. An owning pointer (~int) is a strong reference that cannot be cloned (copying the pointer actually copies the underlying data). A borrowed pointer (&int) is essentially a dangling weak reference that is subject to static restrictions which ensure that it doesn't actually dangle. It is thus primarily a performance optimization. A raw pointer (*int) is a dangling weak reference.
Haskell
もちろんHaskellは弱参照を持っている
Weak t
はhidden keyと型t
の可視値との関連である
doRefWeak theRef
はIO (Maybe t)
である
弱参照は参照先が収集されたときに実行されるoptionalのIO()
を使用して構築できる
このファイナライザは何らかの形でkeyと値を参照することができる
それらを復活させることも明示的に許可されている
ユースケース
弱参照のような機能では、潜在的に対処できる多くの問題がある
同じ言語機能で対処すべきであるかどうかは全く異なる
後方参照
Swiftはcycle-collecting
でないことを考えると、静的参照グラフの最も重要な使用事例は後方参照である
Rを参照するオブジェクトに(おそらく間接的に)強参照を保持するオブジェクトへの参照Rを含む
例としては、
- 双方向連結リスト内の
previousNode
ポインタ - レンダリングツリー内の「親」ポインタ
- 一般的なグラフ構造のエッジ
これらは共通のいくつかのプロパティを保持している
- 強力な参照を使用するには、参照サイクルを分解するための多くの明示的なコードが必要である
- これらの参照は頻繁にアクセスする可能性があるため、パフォーマンスが重要である
- これらの参照を構築の直後に有効にすることは必ずしも実現可能ではない
- 参照先が割り当て解除された後に参照をトラバースすることは、あるものが意図されたよりも長く生きているという調合である可能性が高い。しかしプログラマはクラッシュすることにより根本原因を突き止めるようプログラマに推奨するか、あるいは両方のケースを正しく処理する為の操作を書くだけで、これに対する正しい応答について合理的に異なるかもしれない。最終的にこの選択は哲学に帰結する
キャッシュ
キャッシュが使用可能なすべてのメモリを引き継ぐのを防ぐために、弱いキャッシュが使用される
値の到達可能性に縛られることによってキャッシュはそれらの値が依然として有効であるときに
エントリが誤って期限切れになるのを防ぐ
弱参照を使用することによってキャッシュはシステムがもはや使用されていない値の割り当てを解除することを可能にする
一般に、弱参照を広範囲に使用するデータ構造は、弱参照が収集されたという通知を受け取るために何らかの方法を必要とする
これは値が収集されてもデータ構造内のエントリに大きなオーバーヘッドが発生する可能性があるため
参照が無効にされたという通知を受け取らない弱いデータ構造はこれらのエントリを無期限に累積させるか、
古いエントリを探す構造全体を定期的にスキャンする必要がある
最後の強参照が破棄されたときにその参照先の即時の割り振り解除を可能にする弱参照は、
弱いキャッシュの実装にとって実質的にあまり有用ではない
それは頻繁に何度も検索されるが、各使用が他のものと一時的に乖離しているために、
一般的なアクセスパターン(例えばmemoizing cacheの場合)である
この場合弱参照を単純に使用すると、単にキャッシュにスラッシュが発生してしまう
この問題は非決定論的コレクションを持つ環境では起こりにくい
なぜならエントリがコレクション間で複数のルックアップを処理する可能性が高いからである
弱いデータ構造を実装しているユーザは言語に直接組み込まれているよりも堅牢なシステムよりも、
ゼロ復帰に達するという復活と通知を中心とした柔軟性の高いインフラストラクチャを好む傾向がある
Swiftモデルはメモリスキャナーではなく静的に挿入された操作の周りに構築されているのでこれははるかに実行可能である
TBD
External Finalization
TBD
最適化
TBD
プロポーザルの概要
TBD
提案された変数の属性
TBD
@weak
TBD
@unowned
TBD
TBD
xxx
TBD
TBD
Implementation
TBD
TBD
Declaration Attribute or Type Attribute
TBD
TBD
weak
-Capable Types
TBD
TBD
Generic Weak Support
TBD
TBD
Proposed Rules for Captures within Closures
TBD
TBD
Decorated Capture References
TBD
TBD
capture
Declarations
TBD
TBD
Nested Closures
TBD
TBD
Library Directions
TBD