# 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