How TEngine's AssetReference System Manages Resource Lifecycles in Unity
TEngine's AssetReference system ties Unity asset lifetimes to their host GameObjects through a three-phase process involving reference registration, clone-safety validation, and automatic release upon destruction.
The TEngine framework provides a robust resource management solution for Unity projects through its AssetsReference component. This system eliminates manual memory management boilerplate by automatically handling asset loading and unloading based on GameObject lifecycles. Understanding how TEngine's AssetReference system manages resource lifecycles is essential for preventing memory leaks and optimizing performance in large-scale Unity applications.
Core Architecture of the AssetsReference Component
The AssetsReference class, located at UnityProject/Assets/TEngine/Runtime/Module/ResourceModule/Reference/AssetsReference.cs, serves as the primary mechanism for resource lifecycle management. It operates as a MonoBehaviour that maintains a list of referenced assets and ensures they are released when the host GameObject is destroyed.
The component utilizes a static dictionary _originalRefs to track original asset instances versus cloned instances. This distinction prevents duplicate unload calls and ensures that only legitimate original references trigger resource release operations.
Phase 1: Reference Registration via Ref() Methods
The first phase of lifecycle management occurs when assets are registered through the Ref() or Ref<T>() methods. These methods accept either a GameObject or any UnityEngine.Object and store the reference information in an internal list called refAssetInfoList.
When a source asset is registered, the system captures its instance ID and optionally adds the parent GameObject to the _originalRefs dictionary. This registration allows the system to distinguish between original assets and scene-bound objects that should not be managed by the reference system.
// Registering a prefab asset
GameObject prefab = Resources.Load<GameObject>("UI/ScorePanel");
AssetsReference prefRef = someGameObject.AddComponent<AssetsReference>()
.Ref(prefab);
// Registering a texture asset
Texture2D icon = Resources.Load<Texture2D>("Icons/Health");
someGameObject.AddComponent<AssetsReference>()
.Ref(icon);
Phase 2: Clone Safety with IsOriginalInstance()
The second phase addresses the complexities of prefab instantiation. When a GameObject with an AssetsReference component is cloned, the Awake() method executes and determines whether the current instance is the original or a clone through the IsOriginalInstance() method.
If the instance is identified as a clone, the system invokes ClearCloneReferences() to remove any stale references inherited from the original. This ensures that clones do not accidentally hold references to assets that should be released, preventing double-unload scenarios and memory leaks. Cloned instances start with an empty reference list, while original instances retain their registered assets.
// Instantiating a prefab with automatic reference handling
GameObject instance = AssetsReference.Instantiate(prefab, parentTransform);
// The new instance holds an AssetsReference that will unload the prefab when destroyed
Phase 3: Automatic Resource Release in OnDestroy()
The final phase occurs when the host GameObject is destroyed. The OnDestroy() method guarantees that every registered asset is unloaded through the IResourceModule interface. The process begins with CheckInit() to validate that the resource module is available, followed by CheckRelease() to handle the primary source GameObject.
The system then iterates over all stored AssetsRefInfo entries in refAssetInfoList and calls _resourceModule.UnloadAsset() for each referenced asset. After unloading completes, the internal list is cleared to ensure no dangling references remain. This deterministic unloading eliminates the need for manual cleanup code throughout the application.
// Manual cleanup triggers automatic release
if (instance != null) {
Destroy(instance); // triggers OnDestroy → UnloadAsset(prefab)
}
Lifecycle Guarantees and Safety Mechanisms
The TEngine AssetReference system provides three critical guarantees that prevent common resource management errors in Unity:
-
Deterministic Unloading: All assets registered through
ReforRef<T>are guaranteed to be passed toIResourceModule.UnloadAssetwhen the host GameObject is destroyed, eliminating memory leaks from forgotten references. -
Clone Safety: Cloned GameObjects do not retain stale references from their originals. The
ClearCloneReferences()method ensures each instance manages only its own legitimate references, preventing double-unload exceptions. -
Centralized Module Access: The static
_resourceModulefield is lazily fetched fromModuleSystem.GetModule<IResourceModule>()viaCheckInit(), ensuring the component functions correctly even if the resource module is injected after scene initialization.
Summary
TEngine's AssetReference system manages resource lifecycles by binding Unity asset references to GameObject lifetimes through a three-phase process. The AssetsReference component registers assets via Ref() methods, validates instance originality during Awake() to prevent clone contamination, and automatically releases resources through IResourceModule.UnloadAsset() in OnDestroy(). This architecture eliminates manual memory management while preventing double-unload errors and memory leaks in complex Unity projects.
Frequently Asked Questions
How does TEngine prevent duplicate asset unloading when instantiating prefabs?
TEngine prevents duplicate unloading through the IsOriginalInstance() check in the Awake() method. When a prefab is instantiated, the clone detects that it is not the original instance and calls ClearCloneReferences() to remove inherited references. This ensures only the original asset holder triggers UnloadAsset() when destroyed, while clones start with empty reference lists.
What types of Unity assets can be managed by the AssetsReference component?
The AssetsReference component can manage any UnityEngine.Object type, including GameObject prefabs, Texture2D textures, AudioClip audio files, and other Unity assets. The Ref() method accepts generic UnityEngine.Object parameters, while Ref<T>() provides type-safe registration for specific asset types.
When does the actual asset unloading occur in the lifecycle?
Asset unloading occurs deterministically in the OnDestroy() method of the AssetsReference component. When the host GameObject is destroyed—either through Destroy() calls or scene unloading—the component iterates through its refAssetInfoList and calls _resourceModule.UnloadAsset() for each registered reference. This happens automatically without requiring manual cleanup code.
What happens if the ResourceModule is not initialized when AssetsReference tries to unload assets?
The AssetsReference component includes a CheckInit() method that lazily initializes the static _resourceModule field from ModuleSystem.GetModule<IResourceModule>() when first needed. If the module is not available during OnDestroy(), the check prevents null reference exceptions, though assets may not be properly unloaded if the module was never registered. This defensive programming ensures the component fails gracefully rather than crashing the application.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →