# 3. Loading Instantiating and Releasing Part of [[Addressables]]. Previous: [[2. Groups Profiles and Play Mode Scripts]]. Next: [[4. Labels and Loading Many Assets]]. > [!note] Verification status > APIs and the release rules are from the Addressables **2.9.1** manual ("Load assets", "Unload Addressable assets") and the 2.9.1 Scripting API. `ToAwaitable()` is **not** in 2.9; it is documented for 4.0+. **Code not compiled in Unity.** ## 3.1 The contract Every load has a handle. When you are done with the *asset* (not just one instance), **release the handle**. If you instantiated through Addressables, **release the instance** with the matching API. Forgetting this is the classic Addressables leak, and the Memory Profiler will show the bundle still rooted ([[3. Memory Profiler Snapshots]]). You can: - `yield return handle` in a coroutine - Subscribe to `handle.Completed` - `await handle.Task` (an `AsyncOperationHandle` exposes a `Task` in this API family — confirm on your version if you use it) - On **4.0+ only**, `await handle.ToAwaitable()` The Async Code tutorial later in this queue is the place to convert a production loader to `Awaitable`. Stay on coroutines or `Task` here so 2.9 and 4.x students can share the same sample. ## 3.2 Load, then instantiate yourself ```csharp using System.Collections; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public sealed class LoadHatByAddress : MonoBehaviour { public string address = "cosmetics/hat-red"; AsyncOperationHandle<GameObject> _handle; GameObject _instance; IEnumerator Start() { _handle = Addressables.LoadAssetAsync<GameObject>(address); yield return _handle; if (_handle.Status != AsyncOperationStatus.Succeeded) { Debug.LogError("Failed to load " + address); yield break; } _instance = Instantiate(_handle.Result, transform); } void OnDestroy() { if (_instance != null) Destroy(_instance); if (_handle.IsValid()) _handle.Release(); } } ``` `LoadAssetAsync` can take an address, a label, or other key types. If a label matches *several* assets, **only the first found** is loaded — use `LoadAssetsAsync` (part 4) when you mean "all of them." The 2.9 manual's mutation warning: under **Use Asset Database**, `Result` may be the *project asset*. If you will change materials or mesh data at runtime, `Instantiate` a copy (as above) and still release the handle, not the instance, when you unload. ## 3.3 `InstantiateAsync` When you want Addressables to load *and* instantiate: ```csharp using System.Collections; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public sealed class SpawnHat : MonoBehaviour { public string address = "cosmetics/hat-red"; AsyncOperationHandle<GameObject> _handle; IEnumerator Start() { _handle = Addressables.InstantiateAsync(address, transform); yield return _handle; if (_handle.Status != AsyncOperationStatus.Succeeded) Debug.LogError("Instantiate failed: " + address); } void OnDestroy() { if (_handle.IsValid()) Addressables.ReleaseInstance(_handle); } } ``` Use `ReleaseInstance` (or `Addressables.ReleaseInstance(gameObject)`) for instances you created this way. Do not `Destroy` and forget the handle; do not `Release` a load handle and assume every instance is gone if you also instantiated manually. ## 3.4 `AssetReference` in the Inspector ```csharp using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public sealed class LoadWithReference : MonoBehaviour { public AssetReferenceGameObject hat; void Start() { AsyncOperationHandle<GameObject> handle = hat.LoadAssetAsync<GameObject>(); handle.Completed += op => { if (op.Status == AsyncOperationStatus.Succeeded) Instantiate(op.Result, transform); else Debug.LogError("AssetReference failed: " + hat.RuntimeKey); }; } void OnDestroy() { if (hat != null && hat.Asset != null) hat.ReleaseAsset(); } } ``` `AssetReferenceGameObject` (and other typed references) keep the Inspector from accepting the wrong type. If you assign a non-Addressable asset, Unity will make it Addressable and put it in the default group — glance at the Groups window after you wire references, or your default group becomes a junk drawer. `AssetReference` is how a scene that is *not* itself Addressable can still point at content that is. ## 3.5 Failure and validity Always check `Status` (or `IsValid()` before Release). A failed handle may or may not have a Result. Releasing an invalid handle can throw; guard with `IsValid()`. Do not `Addressables.Release` the same handle twice. Do not cache `Result` after you have released the handle and expect the asset to stay alive — it might, if something else holds it, and it might not. ## Official docs - Load assets (2.9.1): https://docs.unity3d.com/Packages/[email protected]/manual/load-assets.html - Unload (2.9.1): https://docs.unity3d.com/Packages/[email protected]/manual/UnloadingAddressableAssets.html - LoadAssetAsync API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.AddressableAssets.Addressables.LoadAssetAsync.html - InstantiateAsync API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.AddressableAssets.Addressables.InstantiateAsync.html - ToAwaitable (4.x only): https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle-1.ToAwaitable.html