ネイティブコンテナ は値型です。変数に割り当てられると、Unityは NativeContainer 構造体をコピーします。この構造体にはネイティブコンテナのデータを格納する場所へのポインター (AtomicSafetyHandle など) が含まれます。NativeContainer のコンテンツ全体をコピーするわけではありません。
これは、同じメモリ領域を参照する NativeContainer 構造体のコピーが複数存在し、そのすべてに同じ主要レコードを参照する AtomicSafetyHandle オブジェクトが存在する可能性があるということです。
上図に示す NativeArray 構造体の 3 つの異なるコピーは、同じ実際のコンテナを表しています。各コピーは保存された同じデータ、および元の NativeArray と同じ安全データを指しています。ただし、NativeArray の各コピーは、ジョブがそのコピーで実行できる内容を示す互いに異なるフラグを持っています。安全データへのポインターは、フラグとの組み合わせで、AtomicSafetyHandle を構成します。
NativeContainer を破棄する場合、NativeContainer 構造体のすべてのコピーは、元の NativeContainer が無効になることを認識する必要があります。元の NativeContainer を廃棄すると、NativeContainer のデータを保持していたメモリブロックの割り当てが解除されます。この場合、NativeContainer の各コピーに格納されているデータへのポインターは無効になり、使用するとアクセス違反になる可能性があります。
AtomicSafetyHandle も同様に NativeContainer インスタンスで無効になる主要レコードを指しています。ただし、安全システムは主要レコードのメモリ割り当てを解除しないため、アクセス違反のリスクを回避できます。
代わりに、各レコードにはバージョン番号が追加されます。バージョン番号のコピーは、そのレコードを参照する各 AtomicSafetyHandle 内に格納されます。NativeContainer が破棄されると、Unity は Release() を呼び出し、主要レコードのバージョン番号を更新します。こうすることで、このレコードは他の NativeContainer インスタンスに再利用できるようになります。
残りの各 AtomicSafetyHandle は、保存されているバージョン番号と主要レコードのバージョン番号を比較して、NativeContainer が破棄されたかどうかをテストします。Unity は、CheckReadAndThrow や CheckWriteAndThrow などのメソッドの呼び出しの一部として、このテストを自動的に実行します。
動的ネイティブコンテナは、可変サイズのコンテナです。NativeList<T> (Collections パッケージで使用可能) などの要素を継続的に追加できます。これは、NativeArray<T> などの静的ネイティブコンテナがサイズを変更できないのとは対照的です。
動的ネイティブコンテナを使用する場合、ビューと呼ばれる別のインターフェースからデータに直接アクセスすることもできます。ビューを使用すると、NativeContainer オブジェクトのデータをコピーしたり所有権を取得したりすることなく、エイリアスを作成できます。ビューの例として、ネイティブコンテナの要素ごとにデータにアクセスできる Enumerator (列挙子) オブジェクトがあります。また、NativeList を NativeArray のように扱うために使用できる NativeList<T>.AsArray などのメソッドもあります。
動的ネイティブコンテナのサイズを変更する場合、ビューは通常スレッドセーフではありません。これは、ネイティブコンテナのサイズを変更すると、Unity がメモリ内のデータの格納場所を変更し、ビューの格納するポインターが無効になるためです。
動的ネイティブコンテナのサイズの変更に対応するため、安全システムには AtomicSafetyHandle に補助バージョン番号が記録されています。バージョン更新の仕組みと似ていますが、ここでは主要レコードに格納された 2 つ目のバージョン番号を使用し、1 つ目のバージョン番号とは別に更新します。
補助バージョン番号を使用するには、UseSecondaryVersion を使用して、ビューを NativeContainer に保存されているデータに設定します。ネイティブコンテナのサイズを変更する操作や、既存のビューを無効にする操作には、CheckWriteAndThrow ではなく CheckWriteAndBumpSecondaryVersion を使用します。また、ネイティブコンテナへ書き込むジョブがスケジュールされるたびにビューが自動的に無効になるよう、NativeContainer に SetBumpSecondaryVersionOnScheduleWrite を設定する必要があります。
ビューを作成して AtomicSafetyHandle をコピーする場合は、CheckGetSecondaryDataPointerAndThrow を使用して、ネイティブコンテナのメモリーへのポインターをビューにコピーしても安全であることを確認します。
一時的なネイティブコンテナで作業するときに使える特別なハンドルが 2 つあります。
GetTempMemoryHandle: Allocator.Temp で割り当てられたネイティブコンテナで使用できる AtomicSafetyHandle を返します。現在の一時メモリ範囲が終了すると、Unity はこのハンドルを自動的に無効にするため、自分で解除する必要はありません。特定の AtomicSafetyHandle が GetTempMemoryHandle によって返されるハンドルかどうかをテストするには、 IsTempMemoryHandle を使用します。GetTempUnsafePtrSliceHandle: 安全でないメモリで保持された一時的なネイティブコンテナに使用できるグローバルハンドルを返します。例えば、スタックメモリから構築された NativeSlice などです。このハンドルを使用するコンテナをジョブに渡すことはできません。