EditorWindow и SerializedObject.ApplyModifiedProperties(). Как избавиться от ошибки "SerializedObject target has been destroyed"
Имею несколько окошек редактора в которых через диалоги задаю значение свойства. Диалоги большие, с кучей вложенных свойств. В целом работа с окошком выглядит примерно вот так:
using UnityEngine;
using UnityEditor;
public class MyEditorWindow : EditorWindow
{
[MenuItem("Window/My Editor Window")]
public static void ShowWindow()
{
GetWindow<MyEditorWindow>();
}
// список свойств окна к изменению
public string[] Strings = { "Larry", "Curly", "Moe" };
// ссылка на сериализованный объект где будут наши свойства окна
SerializedObject so;
private void OnEnable()
{
//здесь инициируем объект
so = new SerializedObject(this);
}
void OnGUI()
{
so.Update();
//дальше выводим нужный интерфейс через EditorGUILayout, а ля
SerializedProperty stringsProperty = so.FindProperty("Strings");
EditorGUILayout.PropertyField(stringsProperty, true);
so.ApplyModifiedProperties(); // Вот здесь периодически и падает ошибка
}
}
Через поиск встречал, что существует проблема с тем, что при потере фокуса объекта в сцене, а также перехода между диалоговыми окнами в точке где идет обновление свойств объекта so.ApplyModifiedProperties(); частенько редактор генерирует ошибку
SerializedObject target has been destroyed
пробовал заключать отрисовку в BeginChange и EndChange, но это не спасает. На целостность данных в диалогах вроде это не влияет, но раздражает.
Кто-то знает как с этим бороться?
Ответы (3 шт):
Вообще разработчики не хотят что-бы кто-то сам создавал SerializedObject и в документации даже нет описания его свойств, кто такие inspectorMode или inspectorDataMode, которые указываются в Editor.cs... ну хер его знает. Уж тем более писал такую порнуху как new SerializedObject(this) точно никто не предусматривал и бороться тут не с чем.
SerializedObject стримит данные и является IDisposable и за его жизненным циклом нужно следить. Это прописано в классе Editor, у которого есть target для которого создаётся и уничтожается SerializedObject. При использовании в инспекторе, target присваивается автоматически в контексте выбранного объекта, но можно легко делать это и в ручную, если речь не о инспекторе.
По идее и вообще по принципам программирования, модели данных должны быть отделены от визуализации. То есть массив не должен быть среди полей EditorWindow, потому что окно выступает в роли view. В EditorWindow нужно выстраивать композицию из набора Editor просто вызывая их OnGUI и используя EditorGUI по мелочи.
Нашел причину, из-за чего появлялась ошибка. Внутри OnGUI выводился интерфейс и была кнопочка по которой обновлялось свойство в родительском окне и собственно само диалоговое окно закрывалось. Получалось что окно закрыто, но OnGUI еще дорисовывает интерфейс диалога которого нет.
Так что все, что требовалось - это вызывать процедуру закрытия окна после того как интерфейс будет отрисован и отработает so.ApplyModifiedProperties();
Одним из возможных решений может быть следующая последовательность действий:
Вынести нуждающиеся в сериализации поля в отдельный класс, наследующийся от
ScriptableSingleton:// May be useful to save data between editor sessions. // [FilePath("MyEditorWindowContainer.asset", FilePathAttribute.Location.PreferencesFolder)] public class MyEditorWindowContainer : ScriptableSingleton<MyEditorWindowContainer> { public string[] Strings = { "Larry", "Curly", "Moe" }; }В
EditorWindow.OnEnable()получать ссылку на необходимуюSerializedReference:private MyEditorWindowContainer container; private SerializedObject serializedObject; private SerializedProperty stringsProperty; private void OnEnable() { container = MyEditorWindowContainer.instance; serializedObject = new SerializedObject(container); stringsProperty = serializedObject.FindProperty(nameof(container.Strings)); }Далее использовать
SerializedPropertyвOnGUIметоде:void OnGUI() { // Probably is unnecessary, because the container scriptable // object is used by this Editor window only. serializedObject.Update(); EditorGUILayout.PropertyField(stringsProperty, true); serializedObject.ApplyModifiedProperties(); }