Swift 4-弱いリンク

Swiftのソースコードを公開して間もなく、私はウィークリンクの実装方法に関する記事を書きました。 時間は止まらず、すべてが変化します。Swiftでの弱いリンクの実装も変わります。 今日は、新しい実装について説明し、古い実装と比較します。 投稿のアイデアをくれたGuillaume Lessardに感謝します。



古い実装



古い実装を思い出せず、前の記事を読むのが面倒な人のために、ここで簡単に説明します。



Swiftの古いバージョンでは、オブジェクトには2つの参照カウンターがありました。1つは強いリンク用のカウンター、もう1つは弱いリンク用のカウンターです。 強いリンクのカウンターがゼロになるが、弱いリンクがまだない場合、オブジェクトは破棄されますが、メモリは解放されません。 したがって、メモリは、弱いリンクが参照するいわゆる「ゾンビオブジェクト」のままです。



弱いリンクが読み込まれると、ランタイムランタイムはオブジェクトが「ゾンビ」かどうかを確認します。 「ゾンビ」である場合、ランタイムは弱いリンクをリセットし、弱いリンクのカウンターを1減らします。弱いリンクのカウンターが0に達すると、メモリの割り当てが解除されます。 つまり、オブジェクトへのすべての弱参照が無効化された場合にのみ、オブジェクト全体が削除されます。



私はこの実装のシンプルさが気に入りましたが、欠点もありました。



  1. ゾンビオブジェクトは、長時間メモリに残る可能性があります。 この場合、多数のプロパティを持つクラスは非常に多くのメモリを消費します。
  2. 記事を書いた後に発見した別の問題は、実装が同時に読み取りに対してスレッドセーフではないことでした。 痛い! これは修正されましたが、この問題に関して生じた議論は、このような問題に対してより耐性のある弱いリンクの新しい実装を作成するという問題を提起しました。


オブジェクトデータ



Swiftのオブジェクトは多くのデータで構成されています。



まず 、クラスのソースコードで宣言されているすべてのプロパティが含まれていることは明らかです。 これはプログラマーが直接説明するものです。



第二に 、それはクラスオブジェクトを指します。 これは、動的ディスパッチ(C ++で仮想メソッドと呼ばれる)と、実行時にオブジェクトのタイプを決定するための組み込み関数「type(of :)」を実装するために必要です。 これはプログラマからほとんど隠されていますが、仮想メソッドと「(:)」タイプの機能の存在自体はこのメカニズムの存在を前提としています。



第三に 、リンク数が含まれています。 オブジェクトの「生メモリ」の読み取りや、CFGetRetainCountの呼び出しにコンパイラ設定を使用するなどのダーティハックに頼らない場合、これらは完全に隠されます。



4番目に 、すべての弱いリンクのリストや関連オブジェクトなど、Objective-Cランタイム用の追加情報が保存されています。 (Objective-Cに弱いリンクを実装すると、各リンクが個別に追跡されます)



このデータはすべてどこに保存されますか?



Objective-Cでは、クラス(isa)およびプロパティ(プロパティ)へのポインタはオブジェクトのメモリに直接保存されます。 クラスオブジェクトへのポインタは、メモリの最初の部分を占め、その後にインスタンス変数(ivar)が続きます。 追加情報は外部テーブルに保存されます。 関連付けられたオブジェクトが必要な場合、ランタイムは、キーがオブジェクトのアドレスである大きなハッシュテーブルでそれを見つけます。 これには時間がかかり、オブジェクトへのアクセスを同期するには、スレッドによるオブジェクトのキャプチャ(ブロッキング)が必要です。 参照カウンタは、OSのバージョンとプロセッサアーキテクチャに応じて、オブジェクトのメモリに保存されることもあれば、外部テーブルに保存されることもあります。



これがどのように実装されているかを抽象化して、自分自身に質問をしましょう:これはどのように実装されるべきですか?



各メモリロケーションには、長所と短所があります。 オブジェクトのメモリ内に保存されているデータにすばやくアクセスできますが、これは追加のメモリを消費します。 外部表のデータへのアクセスには時間がかかりますが、外部表を必要としないオブジェクトの追加メモリーは消費しません。 これが、Objective-Cが最初に参照カウントをオブジェクトに直接保存しなかった理由の1つです。 Objective-Cは、コンピューターのメモリが非常に限られていたときに開発されました。 典型的なObjective-Cコードのほとんどのオブジェクトの所有者は1人だけです。カウンターは1だと想像できます。常に1を格納する各オブジェクトに4バイトを割り当てるのは無駄です。 外部テーブルでは、合計値1をレコードなしで置き換えることができます。これにより、メモリが節約されます。



クラスオブジェクトは各オブジェクトに関連付けられています。 すべてのメソッド呼び出しに必要です。

したがって、クラスへのポインタはオブジェクト自体に格納されます。別の方法で行うことは有益ではありません。



プロパティへのアクセスは予想どおり高速です。 それらの存在は、コンパイルの段階で決定されます。 プロパティのないオブジェクトは、通常オブジェクトのメモリに格納されている場合でも、メモリを割り当てない場合があります。



サイドテーブル



新しいSwift実装では、サイドテーブルの概念が導入されました。

サイドテーブルは、オブジェクトに関する追加情報を格納する追加のメモリ領域です。 これはオプションです。つまり、必ずしも施設に存在するとは限りません。 それを必要とするオブジェクトはパフォーマンスの低下を被り、それを必要としないオブジェクトはその「負荷」を経験しません。



サイドテーブルにオブジェクトへのポインターがあるように、各オブジェクトにはサイドテーブルへのポインターがあります。 サイドテーブルには、関連オブジェクトなどの追加情報を格納できます。 サイドテーブルへのポインターあたり8バイトの追加コストを回避するために、Swiftは洗練された最適化に頼りました。 最初、オブジェクトの最初のフィールドにはクラスへのポインターが格納され、2番目のフィールドには参照カウントが格納されます。 オブジェクトに外部テーブルが必要な場合、この2番目のフィールドは、オブジェクトへのポインターを格納するために使用され始めます。 いずれにせよ、オブジェクトには参照カウンターが必要なので、現在はサードパーティのテーブルに配置されています。 これらの2つのケースを区別するために、2番目のフィールドに2番目のビットが予約されており、フィールドが参照カウントまたはサイドテーブルへのポインターを格納するかどうかを決定します。



サイドテーブルのおかげで、Swiftはその欠点を排除しながら、古い参照カウントシステムを実装しています。 オブジェクト自体を指すのではなく、弱いリンクがサイドテーブルを指すようになりました。 サイドテーブルはスペースをほとんど占有しないため、大きなオブジェクトへの弱い参照によるメモリの浪費の問題を取り除きます。 これは、スレッドの安全性の問題に対する簡単な解決策につながります。弱いリンクを無効にしないでください。 サイドテーブルはサイズが小さいため、リンク自体が上書きまたは破棄されるまで、サイドテーブルへの弱いリンクを保持できます。



サイドテーブルの現在の実装には、参照カウントとオブジェクト自体へのポインターのみが含まれることに言及する必要があります。 Swiftの関連オブジェクトの実装などの追加のアプリケーションは、まだ仮想的なものです。 Swiftには関連付けられたオブジェクトの機能がまだ組み込まれていないため、Objective-Cは引き続きグローバルテーブルを使用します。



説明したアプローチには大きな可能性があり、おそらくSwiftで関連オブジェクトがすぐに表示されるでしょう。 これにより、クラスの拡張機能やその他の便利な機能にプロパティを保存できるようになります。



コード



Swiftコードは開いているため、視覚的に記述されたすべてのものを見ることができます。

サイドテーブルに関連するすべて

システムに関する完全なコメント付きの弱いリンクの高レベルのインターフェイスは、 ここにあります

ヒープに配置されたオブジェクトに関する実装とコメントは、 ここ見つけることができます



リンクは特定のバージョンのコミットを参照しているので、将来これを読んでいる人は、私が説明していることを理解できます。 最新の実装を探している場合は、リンクをクリックしてから、必ずマスターに切り替えるか、リポジトリを更新してください。



おわりに



弱いリンクはSwiftの重要な機能です。 最初の実装はシンプルでクリーンでしたが、いくつかの問題がありました。 オプションのサイドテーブルを追加することにより、Swift開発者は、以前の実装のメリットを保持しながら、これらの問題を取り除くことができました。 また、将来的にクールな新機能を追加する機会も提供します。 今日はこれですべてです。投稿を希望する場合は、投稿のアイデアを送信してください。



翻訳者から:

何らかのエラー(技術的なものかどうか)に気付いた場合は、これについてコメントを書いてください。

ありがとう



All Articles