【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では、

  1. スキャンされていないヒープメモリ
  2. オブジェクトポインタ型ではなく、__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の後にデータ構造をクリーンアップすることができる

サブクラス化することによって参照オブジェクトに追加データを追加することができる

リファレンスは強度の順に表示される

SoftReferenceVMがメモリ不足になるまでオブジェクトに保持される一種の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 theRefIO (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