Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Releasing TextAsset

Discussion in 'Addressables' started by chad-wmgi, Sep 2, 2020.

  1. chad-wmgi

    chad-wmgi

    Joined:
    Mar 31, 2020
    Posts:
    8
    I have a method the following methods:

    Code (CSharp):
    1. void Start()
    2. {
    3.     Addressables.LoadAssetsAsync<TextAsset>("JSON", null).Completed += InitJson;
    4. }
    5.  
    6. void OnDestroy()
    7. {
    8.     Addressables.Release(m_landmarkFile);
    9. }
    10.  
    11. void InitJson(AsyncOperationHandle<IList<TextAsset>> op)
    12. {
    13.     op.Completed -= InitJson;
    14.  
    15.     if (op.Result != null)
    16.     {
    17.         Debug.LogFormat("Result Count = {0}", op.Result.Count);
    18.         Debug.Log("JSON loaded...");
    19.  
    20.         m_landmarkFile = op.Result.Last();
    21.     }
    22. }
    When I reload the scene that this script is in, then Result Count increments. This makes me think that I didn't successfully release the TextAsset when the script was destroyed. Am I correctly releasing the asset? Is there a way to force an asset to be completely released?
     
  2. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    So TextAssets are the same as any other asset. Normally I would say you should cache the handle rather than the asset, then release the handle. But since you are loading multiple assets with one handle, I'm unsure how unloading a single one of those assets works, or if it's even possible, and you have to unload all of them with the handle.

    +1 to this to see if anyone has an answer.
     
  3. chad-wmgi

    chad-wmgi

    Joined:
    Mar 31, 2020
    Posts:
    8
    The more I'm digging into this problem, the less I'm convinced I can unload assets that are loaded via label. I tried immediately releasing the handle -- which I think would be unloaded, but this doesn't seem to do a thing.

    Code (CSharp):
    1. void InitJson(AsyncOperationHandle<IList<TextAsset>> op)
    2. {
    3.     op.Completed -= InitJson;
    4.     Addresssables.Release(op);
    5.     if (op.Result != null)
    6.     {
    7.         Debug.LogFormat("Result Count = {0}", op.Result.Count);
    8.         Debug.Log("JSON loaded...");
    9.         m_landmarkFile = op.Result.Last();
    10.     }
    11. }
     
    Last edited: Sep 3, 2020
  4. LuGus-Jan

    LuGus-Jan

    Joined:
    Oct 3, 2016
    Posts:
    169
    This is more of thought of my own experience using Addressables so far, so don't take this for absolute truth, but I found that, releasing an individual asset that was loaded through a 'bulk' operation (LoadAssetsAsync), would not release said asset, unless you release the whole handle.

    So I see you use the Last() from the result, caching the actual asset, but not the handle with which it was loaded. To release assets returned in a list, either unload that list, or unload using the handle.

    Would that help?

    For our project, I also created some helper classes for these, as I find keeping track of the handle is somewhat unreliable due to it being a struct, and passing it around, or returning its results never quite did it for me. You're free to use our implementation. Just call
    Dispose
    on it when you're done.

    Interfaces:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public interface IAddressablesLoadingHandle : IEnumerator, IDisposable
    6. {
    7.     bool IsDone
    8.     {
    9.         get;
    10.     }
    11.  
    12.     bool IsSuccess
    13.     {
    14.         get;
    15.     }
    16.  
    17.     bool IsDisposed
    18.     {
    19.         get;
    20.     }
    21.  
    22.     object Result
    23.     {
    24.         get;
    25.     }
    26. }
    27.  
    28. public interface IAddressablesLoadingHandle<T> : IAddressablesLoadingHandle
    29. {
    30.     new T Result
    31.     {
    32.         get;
    33.     }
    34. }
    35.  
    36. public interface IMultiAddressablesLoadingHandle<T> : IAddressablesLoadingHandle
    37. {
    38.     new IList<T> Result
    39.     {
    40.         get;
    41.     }
    42. }
    43.  
    Implementations for all kinds of assets, and MonoBehaviours as well:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using UnityEngine;
    4. using UnityEngine.AddressableAssets;
    5. using UnityEngine.ResourceManagement.ResourceLocations;
    6. using UnityEngine.ResourceManagement.AsyncOperations;
    7.  
    8. /// Generic loading handle for whatever asset you feel like.
    9. public class GenericLoadingHandle<T> : IAddressablesLoadingHandle<T>
    10. {
    11.     protected readonly AsyncOperationHandle<T> loadingHandle;
    12.     private bool disposed = false;
    13.  
    14.     public AsyncOperationHandle<T> LoadingHandle
    15.     {
    16.         get { return loadingHandle; }
    17.     }
    18.  
    19.     public bool IsDone
    20.     {
    21.         get { return loadingHandle.IsDone; }
    22.     }
    23.  
    24.     public bool IsSuccess
    25.     {
    26.         get { return IsDone && (loadingHandle.Status == AsyncOperationStatus.Succeeded); }
    27.     }
    28.  
    29.     public bool IsDisposed
    30.     {
    31.         get { return disposed; }
    32.     }
    33.  
    34.     public T Result
    35.     {
    36.         get { return loadingHandle.Result; }
    37.     }
    38.  
    39.     object IAddressablesLoadingHandle.Result
    40.     {
    41.         get { return Result; }
    42.     }
    43.  
    44.     object IEnumerator.Current
    45.     {
    46.         get { return ((IEnumerator)loadingHandle).Current; }
    47.     }
    48.  
    49.     public GenericLoadingHandle(AsyncOperationHandle<T> loadingHandle)
    50.     {
    51.         if (!loadingHandle.IsValid())
    52.         {
    53.             throw new ArgumentException("The handle is not valid.");
    54.         }
    55.  
    56.         this.loadingHandle = loadingHandle;
    57.     }
    58.  
    59.     public virtual void Dispose()
    60.     {
    61.         if (disposed)
    62.         {
    63.             return;
    64.         }
    65.  
    66.         Addressables.Release(loadingHandle);
    67.         disposed = true;
    68.     }
    69.  
    70.     bool IEnumerator.MoveNext()
    71.     {
    72.         return ((IEnumerator)loadingHandle).MoveNext();
    73.     }
    74.  
    75.     void IEnumerator.Reset()
    76.     {
    77.         throw new NotImplementedException();
    78.     }
    79. }
    80.  
    81. /// For actual Unity assets.
    82. public class AssetLoadingHandle<T> : GenericLoadingHandle<T>
    83. where T : UnityEngine.Object
    84. {
    85.     public AssetLoadingHandle(string assetAddress)
    86.     : base(Addressables.LoadAssetAsync<T>(assetAddress))
    87.     { }
    88.  
    89.     public AssetLoadingHandle(IResourceLocation location)
    90.     : base(Addressables.LoadAssetAsync<T>(location))
    91.     { }
    92.  
    93.     public AssetLoadingHandle(AsyncOperationHandle<T> loadingHandle)
    94.     : base(loadingHandle)
    95.     { }
    96. }
    97.  
    98. /// For loading a single GameObject.
    99. public class GameObjectLoadingHandle : AssetLoadingHandle<GameObject>
    100. {
    101.    public new AsyncOperationHandle<GameObject> LoadingHandle
    102.    {
    103.        get { return loadingHandle; }
    104.    }
    105.  
    106.    public GameObject GameObject
    107.    {
    108.        get { return IsSuccess ? loadingHandle.Result : null; }
    109.    }
    110.  
    111.    public GameObjectLoadingHandle(string assetAddress)
    112.    : base(assetAddress)
    113.    { }
    114.  
    115.    public GameObjectLoadingHandle(AsyncOperationHandle<GameObject> loadingHandle)
    116.    : base(loadingHandle)
    117.    { }
    118. }
    119.  
    120. /// For loading a single MonoBehaviour. Actually just requests the GameObject,
    121. /// but caches the component if it was loaded successfully.
    122. public class MonoBehaviourLoadingHandle<T> : GameObjectLoadingHandle
    123. where T : MonoBehaviour
    124. {
    125.     private T componentCache = null;
    126.  
    127.     public virtual T Component
    128.     {
    129.         get
    130.         {
    131.             if (componentCache != null)
    132.             {
    133.                 return componentCache;
    134.             }
    135.  
    136.             if (IsSuccess)
    137.             {
    138.                 componentCache = GameObject.GetComponent<T>();
    139.             }
    140.  
    141.             return componentCache;
    142.         }
    143.     }
    144.  
    145.     public MonoBehaviourLoadingHandle(string key)
    146.     : base(key)
    147.     { }
    148.  
    149.     public MonoBehaviourLoadingHandle(AsyncOperationHandle<GameObject> loadingHandle)
    150.     : base(loadingHandle)
    151.     { }
    152.  
    153.     public override void Dispose()
    154.     {
    155.         componentCache = null;
    156.         base.Dispose();
    157.     }
    158. }
    159.  
    160. /// Generic loading handle for loading multiple assets at once.
    161. public class MultiAssetLoadingHandle<T> : IMultiAddressablesLoadingHandle<T>
    162. where T : UnityEngine.Object
    163. {
    164.    protected AsyncOperationHandle<IList<IResourceLocation>> locationLoadingHandle;
    165.    protected AsyncOperationHandle<IList<T>> assetsLoadingHandle;
    166.    private bool isDisposed = false;
    167.    private bool isDone = false;
    168.  
    169.    public AsyncOperationHandle<IList<IResourceLocation>> ResourceLocationLoadingHandle
    170.    {
    171.        get { return locationLoadingHandle; }
    172.    }
    173.  
    174.    public AsyncOperationHandle<IList<T>> AssetsLoadingHandle
    175.    {
    176.        get { return assetsLoadingHandle; }
    177.    }
    178.  
    179.    public bool IsDone
    180.    {
    181.        get { return isDone; }
    182.    }
    183.  
    184.    public bool IsSuccess
    185.    {
    186.        get
    187.        {
    188.            return
    189.                IsDone &&
    190.                (assetsLoadingHandle.Status == AsyncOperationStatus.Succeeded) &&
    191.                (locationLoadingHandle.Status == AsyncOperationStatus.Succeeded);
    192.        }
    193.    }
    194.  
    195.    public bool IsDisposed
    196.    {
    197.        get { return isDisposed; }
    198.    }
    199.  
    200.    public IList<T> Result
    201.    {
    202.        get { return assetsLoadingHandle.Result; }
    203.    }
    204.  
    205.    object IAddressablesLoadingHandle.Result
    206.    {
    207.        get { return Result; }
    208.    }
    209.  
    210.    object IEnumerator.Current
    211.    {
    212.        get
    213.        {
    214.            if (!locationLoadingHandle.IsDone)
    215.            {
    216.                return ((IEnumerator)locationLoadingHandle).Current;
    217.            }
    218.            else if (assetsLoadingHandle.IsValid() && !assetsLoadingHandle.IsDone)
    219.            {
    220.                return ((IEnumerator)assetsLoadingHandle).Current;
    221.            }
    222.            else
    223.            {
    224.                return null;
    225.            }
    226.        }
    227.    }
    228.  
    229.    public MultiAssetLoadingHandle(IList<string> keys)
    230.    : this(keys, Addressables.MergeMode.Union)
    231.    { }
    232.  
    233.    public MultiAssetLoadingHandle(IList<string> keys, Addressables.MergeMode mergeMode)
    234.    {
    235.        List<object> objKeys = new List<object>(keys);
    236.        this.locationLoadingHandle = Addressables.LoadResourceLocationsAsync(objKeys, mergeMode, typeof(T));
    237.        locationLoadingHandle.Completed += OnLocationLoadingCompleted;
    238.    }
    239.  
    240.    public MultiAssetLoadingHandle(AsyncOperationHandle<IList<IResourceLocation>> locationLoadingHandle)
    241.    {
    242.        if (!locationLoadingHandle.IsValid())
    243.        {
    244.            throw new ArgumentException("The handle is not valid.");
    245.        }
    246.  
    247.        this.locationLoadingHandle = locationLoadingHandle;
    248.        locationLoadingHandle.Completed += OnLocationLoadingCompleted;
    249.    }
    250.  
    251.    public virtual void Dispose()
    252.    {
    253.        if (isDisposed)
    254.        {
    255.            return;
    256.        }
    257.  
    258.        if (assetsLoadingHandle.IsValid())
    259.        {
    260.            Addressables.Release(assetsLoadingHandle);
    261.        }
    262.  
    263.        if (locationLoadingHandle.IsValid())
    264.        {
    265.            Addressables.Release(locationLoadingHandle);
    266.        }
    267.  
    268.        isDone = true;
    269.        isDisposed = true;
    270.    }
    271.  
    272.    private void OnLocationLoadingCompleted(AsyncOperationHandle<IList<IResourceLocation>> r)
    273.    {
    274.        if (locationLoadingHandle.Status != AsyncOperationStatus.Succeeded)
    275.        {
    276.            Debug.LogErrorFormat("The loading of resource locations in loadinghandle of type '{0}' did not complete successfully.", this.GetType().Name);
    277.            isDone = true;
    278.            return;
    279.        }
    280.  
    281.        assetsLoadingHandle = Addressables.LoadAssetsAsync<T>(r.Result, null);
    282.        assetsLoadingHandle.Completed += OnAssetsLoadingCompleted;
    283.    }
    284.  
    285.    private void OnAssetsLoadingCompleted(AsyncOperationHandle<IList<T>> r)
    286.    {
    287.        isDone = true;
    288.    }
    289.  
    290.    bool IEnumerator.MoveNext()
    291.    {
    292.        if (locationLoadingHandle.IsValid() && !locationLoadingHandle.IsDone)
    293.        {
    294.            ((IEnumerator)locationLoadingHandle).MoveNext();
    295.        }
    296.  
    297.        if (assetsLoadingHandle.IsValid() && !assetsLoadingHandle.IsDone)
    298.        {
    299.            ((IEnumerator)assetsLoadingHandle).MoveNext();
    300.        }
    301.  
    302.        return !IsDone;
    303.    }
    304.  
    305.    void IEnumerator.Reset()
    306.    {
    307.        throw new NotImplementedException();
    308.    }
    309. }
    310.  
    311. /// For loading multiple GameObjects at once.
    312. public class MultiGameObjectLoadingHandle : MultiAssetLoadingHandle<GameObject>
    313. {
    314.    public IList<GameObject> GameObjects
    315.    {
    316.        get { return IsSuccess ? assetsLoadingHandle.Result : null; }
    317.    }
    318.  
    319.    public MultiGameObjectLoadingHandle(AsyncOperationHandle<IList<IResourceLocation>> locationLoadingHandle)
    320.    : base(locationLoadingHandle)
    321.    { }
    322.  
    323.    public MultiGameObjectLoadingHandle(IList<string> keys)
    324.    : base(keys)
    325.    { }
    326.  
    327.    public MultiGameObjectLoadingHandle(IList<string> keys, Addressables.MergeMode mergeMode) : base(keys, mergeMode)
    328.    { }
    329. }
    330.  
    331. /// For loading multiple MonoBehaviours at once. Uses the Multi GameObject variant
    332. /// to load the prefabs and caches the MonoBehaviours when the result completed successfully.
    333. public class MultiMonoBehaviourLoadingHandle<T> : MultiGameObjectLoadingHandle
    334. where T : MonoBehaviour
    335. {
    336.    private IList<T> components = null;
    337.  
    338.    public virtual IList<T> Components
    339.    {
    340.        get
    341.        {
    342.            if (components != null)
    343.            {
    344.                return components;
    345.            }
    346.  
    347.            if (IsSuccess)
    348.            {
    349.                components = new List<T>(GameObjects.Count);
    350.                foreach (GameObject obj in GameObjects)
    351.                {
    352.                    T component = obj.GetComponent<T>();
    353.                    if (component != null)
    354.                    {
    355.                        components.Add(component);
    356.                    }
    357.                }
    358.            }
    359.  
    360.            return components;
    361.        }
    362.    }
    363.  
    364.    public MultiMonoBehaviourLoadingHandle(AsyncOperationHandle<IList<IResourceLocation>> locationLoadingHandle)
    365.    : base(locationLoadingHandle)
    366.    { }
    367.  
    368.    public MultiMonoBehaviourLoadingHandle(IList<string> keys)
    369.    : base(keys)
    370.    { }
    371.  
    372.    public MultiMonoBehaviourLoadingHandle(IList<string> keys, Addressables.MergeMode mergeMode) : base(keys, mergeMode)
    373.    { }
    374. }
    375.  
     
    Last edited: Sep 3, 2020