iosunity-game-enginememory-leaksassetbundlescene-manager

Unity iOS crash when unloading assetbundle scene


I have separated some mini games in another project to load them all in one project. I have created asset bundles successfully with Unity 2018.4.9 version and put them into StreamingAssets folder. Unity Editor loads and unloads scenes successfully but when I build IOS, loads scene successfully, no problem, but when I want to unload it, It gives me memory error like that :

I think this error occurs when system tries to delete a gameobject related with asset bundles. Because when I want to delete all gameobjects in scene before unloading scene, it gives same error. This error only occurs in iOS, not Editor. Unity Editor unloads asset bundle scenes succesfully. Here my code to load/unload asset bundles and their scenes :

Error occurs when I run UnloadAdditiveScene()

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class MarketplaceBundleGameLoader : MonoBehaviour
{
    AssetBundle myLoadAssetBundle;
    AssetBundle[] assetBundles;
    float progress = 0f;
    List<GameObject> objectsToBeDisabled;

    public bool passingToAnotherScene = false;
    public static string bundlePath;
    public static MarketplaceBundleGameLoader Instance;
    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            objectsToBeDisabled = new List<GameObject>();
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            DestroyImmediate(gameObject);
        }
    }

    public void AddObjectToBeDisabled(GameObject go)
    {
        objectsToBeDisabled.Add(go);
    }

    public void LoadMultipleBundles(params string[] pathes)
    {
        if (passingToAnotherScene)
        {
            return;
        }
        passingToAnotherScene = true;
        StartCoroutine(LoadMultipleBundlesTask(pathes));
    }

    IEnumerator LoadMultipleBundlesTask(params string[] pathes)
    {
        progress = 0;
        assetBundles = new AssetBundle[pathes.Length];
        Resources.UnloadUnusedAssets();
        yield return FaderManager.Instance.CloseTheatre();
        for (int i = 0; i < objectsToBeDisabled.Count; i++)
        {
            objectsToBeDisabled[i].SetActive(false);
        }
        SceneManager.LoadScene("BundleGameLoader", LoadSceneMode.Additive);
        yield return FaderManager.Instance.OpenTheatre();
        for (int i = 0; i < pathes.Length; i++)
        {
            AssetBundleCreateRequest abcr = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(Path.Combine(StaticVariables.GetStreamingPath(), pathes[i])));
            while (abcr.progress < 0.9f)
            {
                progress = (i / (1f + pathes.Length)) + abcr.progress / (1f + pathes.Length);
                yield return new WaitForFixedUpdate();
            }
            assetBundles[i] = abcr.assetBundle;
        }
        string[] scenePath = assetBundles[0].GetAllScenePaths();
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(System.IO.Path.GetFileNameWithoutExtension(scenePath[0]), LoadSceneMode.Additive);
        asyncLoad.allowSceneActivation = false;
        float totalTimer = 0f;
        while (asyncLoad.progress < 0.9f)
        {

            totalTimer += Time.deltaTime;
            progress = ((float)pathes.Length / (pathes.Length + 1)) + asyncLoad.progress / (pathes.Length + 1);
            yield return null;
        }
        progress = 1.05f;
        float waitDifferenceTime = Mathf.Max(0.1f, 3f - totalTimer);
        yield return new WaitForSeconds(waitDifferenceTime);
        Resources.UnloadUnusedAssets();
        yield return FaderManager.Instance.CloseTheatre();
        asyncLoad.allowSceneActivation = true;

        AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(SceneManager.GetSceneByName("BundleGameLoader"));
        while (asyncUnload.progress < 0.9f)
        {
            yield return null;
        }
        passingToAnotherScene = false;
        yield return FaderManager.Instance.OpenTheatre();
    }


    public float GetProgress()
    {
        return progress;
    }

    public void UnloadAdditiveScene(string unloadName)
    {
        StartCoroutine(UnloadAdditiveSceneSceneTask(unloadName));
    }

    IEnumerator UnloadAdditiveSceneSceneTask(string unloadName)
    {
        yield return FaderManager.Instance.CloseTheatre();
        for (int i = 0; i < assetBundles.Length; i++)
        {
            assetBundles[i].Unload(false);
        }
        yield return new WaitForEndOfFrame();
        AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(unloadName);
        while(!asyncUnload.isDone)
        {
            yield return new WaitForEndOfFrame();
        }
        yield return new WaitForEndOfFrame();
        SceneManager.SetActiveScene(SceneManager.GetSceneAt(0));
        //AssetBundle.UnloadAllAssetBundles(true);
        // AssetBundle[] bundles = Resources.FindObjectsOfTypeAll<AssetBundle>();
        // //Debug.Log(bundles.Length);
        // for (int i = 1; i < bundles.Length; i++)
        // {
        //     bundles[i].Unload(true);
        // }
        //Resources.UnloadUnusedAssets();
        for (int i = 0; i < objectsToBeDisabled.Count; i++)
        {
            objectsToBeDisabled[i].SetActive(true);
        }
        objectsToBeDisabled.Clear();

        yield return FaderManager.Instance.OpenTheatre();
    }

    // public void loadScene(string sceneName)
    // {
    //  if(passingToAnotherScene)
    //  {
    //      return;
    //  }
    //  passingToAnotherScene = true;
    //  StartCoroutine(loadSceneTask(sceneName));
    // }

    // public IEnumerator loadSceneTask(string sceneName)
    // {
    //  yield return FaderController.Instance.fadeScreen(0.5f);
    //     yield return new WaitForSeconds(0.5f);

    //  SceneManager.LoadScene("SeasonsLoading");

    //  yield return FaderController.Instance.unfadeScreen(0.5f);
    //  AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
    //     asyncLoad.allowSceneActivation = false;
    //     float totalTimer = 0f;
    //     while(asyncLoad.progress < 0.9f)
    //     {
    //         totalTimer += Time.deltaTime;
    //         yield return null;
    //     }
    //     float waitDifferenceTime = Mathf.Max(0.1f, 3f - totalTimer);
    //     yield return new WaitForSeconds(waitDifferenceTime);
    //  yield return FaderController.Instance.fadeScreen(0.5f);
    //  asyncLoad.allowSceneActivation = true;
    //  passingToAnotherScene = false;
    //  yield return FaderController.Instance.unfadeScreen(0.5f);

    //     //yield return new WaitForSeconds(0.5f);


    // }

    public void RestartScene(string sceneName)
    {
        if (passingToAnotherScene)
        {
            return;
        }
        passingToAnotherScene = true;
        StartCoroutine(RestartSceneTask(sceneName));
    }

    public IEnumerator RestartSceneTask(string sceneName)
    {
        yield return FaderManager.Instance.CloseTheatre();
        AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(SceneManager.GetSceneByName(sceneName));
        while (asyncUnload.progress < 0.9f)
        {
            yield return null;
        }
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
        while (asyncLoad.progress < 0.9f)
        {
            yield return null;
        }
        passingToAnotherScene = false;
        yield return FaderManager.Instance.OpenTheatre();
    }


    public void ChangeScene(string unloadScene, string loadScene)
    {
        if (passingToAnotherScene)
        {
            return;
        }
        passingToAnotherScene = true;
        StartCoroutine(ChangeSceneTask(unloadScene, loadScene));
    }

    public IEnumerator ChangeSceneTask(string unloadScene, string loadScene)
    {
        yield return FaderManager.Instance.CloseTheatre();
        AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(SceneManager.GetSceneByName(unloadScene));
        while (asyncUnload.progress < 0.9f)
        {
            yield return null;
        }
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(loadScene, LoadSceneMode.Additive);
        while (asyncLoad.progress < 0.9f)
        {
            yield return null;
        }
        passingToAnotherScene = false;
        yield return FaderManager.Instance.OpenTheatre();
    }
}

Solution

  • Disabling "Strip Engine Code" option in Player Settings solved my problem. In 2018.3.4, when you disable this option, Xcode gives linker error. But in 2019.2.6f, it works.