# 实现自定义原生容器

> Implement custom native containers.

要实现自定义原生容器，必须使用 [`NativeContainer`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/native-container-attribute.md) 属性对类型进行注释。您还应该理解原生容器与[安全系统](/engine/6000.0/manual/scripting/optimization/job-system/native-container.md#safety-system)集成的方式。

要实现的主要元素有二：

* \*\*占用跟踪：\*\*允许 Unity 跟踪使用 `NativeContainer` 实例的被调度作业，以便检测并防止潜在冲突，例如两个作业同时对同一原生容器进行写入。
* \*\*泄漏跟踪：\*\*检测未正确弃置 `NativeContainer` 的情况。在这种情况下会发生内存泄漏，此时，分配给 `NativeContainer` 的内存会在程序的整个剩余生命周期内都不可用。

## 实现占用跟踪

要在代码中访问占用跟踪，请使用 [`AtomicSafetyHandle`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle.md) 类。`AtomicSafetyHandle` 包含对安全系统为给定原生容器存储的核心信息的引用，也是 `NativeContainer` 方法与安全系统交互的主要方式。也因为这一点，所有 `NativeContainer` 实例都必须包含一个名为 `m_Safety` 的 `AtomicSafetyHandle` 字段。

所有·`AtomicSafetyHandle` 都存储了一组标志，表示在当前上下文中可对原生容器执行的操作类型。当作业中包含 `NativeContainer` 实例时，作业系统会自动配置 `AtomicSafetyHandle` 中的标志，以反映原生容器在该作业中的使用方式。

当作业尝试通过 `NativeContainer` 实例进行读取时，作业系统会在读取前调用 `CheckReadAndThrow` 方法，以确认作业对原生容器具有读取权限。同样，当作业尝试对原生容器进行写入时，作业系统会在写入前调用 `CheckWriteAndThrow`，以确认作业对原生容器具有写入权限。至于分配了相同 `NativeContainer` 实例的两个作业，它们针对该原生容器具有单独的 `AtomicSafetyHandle` 对象，因此，尽管它们引用的是同一组核心信息，但它们可以持有不同的标志，以单独指示各作业对原生容器的读写权限。

## 实现泄漏跟踪

泄漏跟踪主要由 Unity 的原生代码实现。Unity 使用 [`UnsafeUtility.MallocTracked`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/unsafe-utility/malloc-tracked.md) 方法来分配存储 `NativeContainer` 数据所需的内存，然后使用 [`UnsafeUtility.FreeTracked`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/unsafe-utility/free-tracked.md) 对其进行弃置。

在 Unity 的早期版本中，提供泄漏跟踪的是 [`DisposeSentinel`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/dispose-sentinel.md) 类。Unity 会在[垃圾回收器](/engine/6000.0/manual/analysis/performance-memory-overview/managed-memory/garbage-collector.md)收集 `DisposeSentinel` 对象时报告内存泄漏。要创建 `DisposeSentinel`，请使用 `Create` 方法，该方法也会同时初始化 `AtomicSafetyHandle`。使用此方法时，无需初始化 `AtomicSafetyHandle`。当 `NativeContainer` 被弃置时，`Dispose` 方法会通过一次调用同时处置 `DisposeSentinel` 和 `AtomicSafetyHandle`。

要识别被泄漏 `NativeContainer` 的创建位置，可以捕获内存最初分配位置的堆栈跟踪。为此，请使用 [`NativeLeakDetection.Mode`](/engine/6000.0/script-reference/unity-collections/native-leak-detection/mode.md) 属性。您还可以通过编辑器访问此属性。为此，请转到**偏好设置 (Preferences)** > **作业 (Jobs)** > **泄漏检测级别 (Leak Detection Level)**，然后选择所需的级别。

## 嵌套原生容器

安全系统不支持在作业中使用嵌套原生容器，因为作业系统无法为大型 `NativeContainer` 实例中的各个单独 `NativeContainer` 正确配置 `AtomicSafetyHandle`。

要防止调度使用嵌套原生容器的作业，请使用 `SetNestedContainer`，当 `NativeContainer` 包含其他 `NativeContainer` 实例时，它会将其标记为嵌套。

## 安全 ID 和错误消息

安全系统会提供错误消息来指示代码不符合安全约束的情况。为让错误消息更为清晰易懂，您可以在安全系统中注册 `NativeContainer` 对象的名称。

要注册名称，请使用 [`NewStaticSafetyId`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/new-static-safety-id.md) 以返回可以传递给 [`SetStaticSafetyId`](ScriptRef:Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.SetStaticSafetyIdl) 的安全 ID。安全 ID 后创建就可以在 `NativeContainer` 的所有实例中重复使用，因此常见的模式是将其存储在容器类的静态成员中。

还可以使用 [`SetCustomErrorMessage`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/set-custom-error-message.md) 覆盖特定安全约束违规的错误消息。

## 其他资源

* [复制 NativeContainer 结构](/engine/6000.0/manual/scripting/optimization/job-system/native-container/copy-nativecontainer.md)
* [自定义 NativeContainer 示例](/engine/6000.0/manual/scripting/optimization/job-system/native-container/custom-nativecontainer-example.md)
