从 Application.persistentDataPath 保存和加载数据在 Unity 中有效,但在 iOS 中无效

Save and Load Data from Application.persistentDataPath works in Unity, but not on iOS

我正在构建一个需要将用户创建的数据保存和加载到持久数据路径的应用程序。当我在 Unity Editor 中播放它时它完美运行,但是当我在 iOS 上尝试它时,我得到这个错误:

Uploading Crash Report
NullReferenceException: Object reference not set to an instance of an object.
  at CustomModeScript.LoadSavedModes () [0x00000] in <00000000000000000000000000000000>:0 
  at CustomModeScript.Initialize (System.String fromPanel) [0x00000] in <00000000000000000000000000000000>:0 
  at MainSettingsPanelScript.OnControlModes () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.Events.UnityAction.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1].Invoke (T1 handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, System.Boolean pressed, System.Boolean released) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in <00000000000000000000000000000000>:0 
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()

以前是ioexception: Sharing violation on path错误,现在是我重构我的Load函数后的上面的错误。当我尝试加载数据时出现了这个问题(我真的不知道它是否正在保存)

这是保存和加载(检索)的函数

public void SaveModes()
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/UserCustomModes.jl");
        List<Mode> data = new List<Mode>();

        data = savedModes;
        
        bf.Serialize(file , data);
        file.Close();
    }

    // Retrieve saved modes if they exist 
    public List<Mode> RetrieveSavedModes()
    {
        if(File.Exists(Application.persistentDataPath + "/UserCustomModes.jl"))
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Open(Application.persistentDataPath + "/UserCustomModes.jl", FileMode.Open);
            List<Mode> storedModesFile = bf.Deserialize(file) as List<Mode>;

            savedModes = storedModesFile;
            file.Close();
        }

        return savedModes;
    }

调用加载函数的代码如下:

// Populate mode view with saved modes
    private void PopulateModeView()
    {
        ClearCustomModes();
        Debug.Log("Loading Modes: " + Manager.savedModes.Count);
        if (Manager.savedModes.Count > 0)
        {
            //Debug.Log("More than 0 modes present");
            var index = 0;
            foreach (var savedMode in Manager.savedModes)
            {
                var modeItem = Instantiate(customModeItemPrefab, customModeSectionTransform);
                modeItem.GetComponent<CustomModeItemScript>().Initialize(savedMode, index, this, mainSettingsPanelScript);
                generatedModeButtons.Add(modeItem);
            }
        }
    }

这是 LoadSavedModes 函数:

private void LoadSavedModes()
    {
        RemoveAllModeButtons();
        Manager.savedModes = Manager.RetrieveSavedModes();
        Debug.Log("[UNITY] Retrieve Modes Count: " + Manager.savedModes.Count);
        PopulateModeView();
    }

提前致谢!

这是我的个人模板 Save/Load 脚本。我知道它适用于函数 GetFilePath.

当前列出的所有平台
using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;

/// <summary>
/// Saves, loads and deletes all data in the game
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SaveLoad<T>
{
    /// <summary>
    /// Save data to a file (overwrite completely)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="data"></param>
    /// <param name="folder"></param>
    /// <param name="file"></param>
    public static void Save(T data, string folder, string file)
    {
        // get the data path of this save data
        string dataPath = GetFilePath(folder, file);

        string jsonData = JsonUtility.ToJson(data, true);
        byte[] byteData;
        
        byteData = Encoding.ASCII.GetBytes(jsonData);

        // create the file in the path if it doesn't exist
        // if the file path or name does not exist, return the default SO
        if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(dataPath));
        }

        // attempt to save here data
        try
        {
            // save datahere
            File.WriteAllBytes(dataPath, byteData);
            Debug.Log("Save data to: " + dataPath);
        }
        catch (Exception e)
        {
            // write out error here
            Debug.LogError("Failed to save data to: " + dataPath);
            Debug.LogError("Error " + e.Message);
        }
    }
    
    /// <summary>
    /// Load all data at a specified file and folder location
    /// </summary>
    /// <param name="folder"></param>
    /// <param name="file"></param>
    /// <returns></returns>
    public static T Load(string folder, string file)
    {
        // get the data path of this save data
        string dataPath = GetFilePath(folder, file);

        // if the file path or name does not exist, return the default SO
        if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
        {
            Debug.LogWarning("File or path does not exist! " + dataPath);
            return default(T);
        }

        // load in the save data as byte array
        byte[] jsonDataAsBytes = null;

        try
        {
            jsonDataAsBytes = File.ReadAllBytes(dataPath);
            Debug.Log("<color=green>Loaded all data from: </color>" + dataPath);
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed to load data from: " + dataPath);
            Debug.LogWarning("Error: " + e.Message);
            return default(T);
        }

        if (jsonDataAsBytes == null)
            return default(T);

        // convert the byte array to json
        string jsonData;

        // convert the byte array to json
        jsonData = Encoding.ASCII.GetString(jsonDataAsBytes);

        // convert to the specified object type
        T returnedData = JsonUtility.FromJson<T>(jsonData);

        // return the casted json object to use
        return (T)Convert.ChangeType(returnedData, typeof(T));
    }
    
    /// <summary>
    /// Create file path for where a file is stored on the specific platform given a folder name and file name
    /// </summary>
    /// <param name="FolderName"></param>
    /// <param name="FileName"></param>
    /// <returns></returns>
    private static string GetFilePath(string FolderName, string FileName = "")
    {
        string filePath;
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
        // mac
        filePath = Path.Combine(Application.streamingAssetsPath, ("data/" + FolderName));

        if (FileName != "")
            filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
        // windows
        filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));

        if(FileName != "")
            filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_ANDROID
        // android
        filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));

        if(FileName != "")
            filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_IOS
        // ios
        filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));

        if(FileName != "")
            filePath = Path.Combine(filePath, (FileName + ".txt"));
#endif
        return filePath;
    }
}

这是一个使用脚本的例子

[System.Serializable]
public class TestClass
{
    public TestClass()
    {
         testData = new List<int>();
    }

    public List<int> testData = new List<int>();
}

public class TestMono : MonoBehaviour
{
     private string testFolder = "folder";
     private string testFile = "file";

     private List<int> myList = new List<int>();

     private void Awake()
     {
          // the ?? is a null comparison so if the value returns null, it generates new data
          TestClass loadedData = SaveLoad<TestClass>.Load(folder, file) ?? new TestClass();

          myList = loadedData.testData;
     }

     private void SaveData()
     {
         TestClass dataToSave = new TestClass
         {
              testData = myList
         };
          
         SaveLoad<TestClass>.Save(dataToSave, folder, file);
     }
}

以上代码段未经测试,但它是 Save/Load 脚本的一般用例。