Комментарии 13
Ну так уже много раз сказано и написано статей(даже на хабре), что синглтон нужно использовать очень осмотрительно, а лучше вообще не использовать. Сначала он выглядит просто и красиво, а чуть поже начинаются приседания. Что ваша статья добавляет ко всему сказаному?
SinglEton, Карл. SinglE. Исправь, везде сингел.
Не верю, что ничего подобного по синглтонам на Хабре не было. Даже если не здесь, то по интернетам подобной инфы размазано в изобилии. Любимая цацка начинающих юнитистов же!
FYI, на будущее, большая часть вот этой вот простынки:
Успешно заменяется на:
FYI, на будущее, большая часть вот этой вот простынки:
//Find T
T[] managers = GameObject.FindObjectsOfType<T>();
if (managers != null)
{
if (managers.Length == 1)
{
instance = managers[0];
DontDestroyOnLoad(instance);
return instance;
}
else
{
if (managers.Length > 1)
{
Debug.LogError($"Have more that one {typeof(T).Name} in scene. " + "But this is Singleton! Check project.");
for (int i = 0; i < managers.Length; ++i)
{
T manager = managers[i];
Destroy(manager.gameObject);
}
}
}
}
Успешно заменяется на:
FindObjectsOfType<T>().SingleOrDefault()
Вот этого не понял.
В одном случае мы проверяем, что на сцене нет объектов, которые создают ложные синглтоны (например при переходе между сценами оставили какой-то менеджер). И устраняем эти ложные объекты. Например при работе со фоновым звуком, в каждой сцене не надо иметь менеджер звука — нужно только его вызывать. И еще интересный момент — успешная замена в итоге вызовет исключение, если будет найдено более 1 объекта. То есть мы еще и поимеем обработку исключений. Адекватно ли это? FYI, абсолютно нет.
Касательно размазано — да так и есть, поэтому я предоставил ссылки, где предоставлен весь этот код. Я просто собрал его вместе и пояснил в чем его удобство. На гениальность не претендую)
В одном случае мы проверяем, что на сцене нет объектов, которые создают ложные синглтоны (например при переходе между сценами оставили какой-то менеджер). И устраняем эти ложные объекты. Например при работе со фоновым звуком, в каждой сцене не надо иметь менеджер звука — нужно только его вызывать. И еще интересный момент — успешная замена в итоге вызовет исключение, если будет найдено более 1 объекта. То есть мы еще и поимеем обработку исключений. Адекватно ли это? FYI, абсолютно нет.
Касательно размазано — да так и есть, поэтому я предоставил ссылки, где предоставлен весь этот код. Я просто собрал его вместе и пояснил в чем его удобство. На гениальность не претендую)
Убивать невесть откуда взявшиеся дополнительные инстансы в рантайме, по-моему, весьма сомнительная идея — логичнее кинуть исключение и заставить автора фиксить логику, порождающую эти самые лишние инстансы, не?
Так смысл синглтона в том, что он 1 будет на весь проект. Мы и так кинем сообщение об ошибке, но приложение останется работоспособным и не упадет. И убивать дополнительные инстансы является верным делом, так как они просто будут сидеть в памяти и все. А за счет DontDestroyOnLoad объект не будет почищен уборщиком памяти и просто будет висеть. У меня все приложении должны работать 24/7 без присмотра пользователя, если я оставлю exception, то при вызове какого-нибудь захудалого менеджера раз в 2-3 часа будет вызывать падение приложения. Или писать логику на обработчик ошибок (каждый раз разную). Такие доводы.
Мой комментарий выше был о том, чтобы в первую очередь не создавать лишние сущности — принцип, выполнение которого в данном конкретном случае можно обеспечить с помощью исключений. Вы же не будете индексировать массив с аргументом -1, правильно? Потому что среда выполнения выбросит исключение, а не молчаливо с лицом okay.jpg перетерпит. Вот тут то же самое. И это не что-то неконтролируемое, как, например, кривой JSON, пришедший от сервера, тут всё в Ваших руках. И Вам же будет лучше, не мне.
И да, DontDestroyOnLoad() не имеет отношение к сборщику мусора, если, конечно, Вы его имели в виду.
И да, DontDestroyOnLoad() не имеет отношение к сборщику мусора, если, конечно, Вы его имели в виду.
Смешно сказать, но очень часто индексирую (часто делают в обработке изображений для правильной обработки краев).
Логика в исключении есть — но давайте тогда подумаем, что мы за этим тянем? Дополнительная обработка исключений — кто-то должен отловить и обработать эту ошибку (а кто?). В итоге мы придем к тому же коду, только оформленному иначе.
Касательно DontDestroyOnLoad() -> он тащит за собой следующий вызов
Если лезть дальше, то просто помечается объект, не удалятся (KeepAlive()). И если мы накидаем несколько на сцену объектов одного класса, то когда будут уничтожены эти объекты? При выходе из приложения.
Как я это проверил — наследовал вышеуказанный синглтон и просто добавил в него счетчик созданных объектов (в конструктор и деструктор(финализатор)).
Вообще логика мне ваша понятна, но в данном случае я предпочитаю сделать так, чтобы точно держать в памяти 1 объект.
Логика в исключении есть — но давайте тогда подумаем, что мы за этим тянем? Дополнительная обработка исключений — кто-то должен отловить и обработать эту ошибку (а кто?). В итоге мы придем к тому же коду, только оформленному иначе.
Касательно DontDestroyOnLoad() -> он тащит за собой следующий вызов
/// /// Do not destroy the target Object when loading a new Scene.
///
/// An Object not destroyed on Scene change.
[MethodImpl(MethodImplOptions.InternalCall)]
[FreeFunction(«GetSceneManager().DontDestroyOnLoad»)]
public static extern void DontDestroyOnLoad(Object target);
Если лезть дальше, то просто помечается объект, не удалятся (KeepAlive()). И если мы накидаем несколько на сцену объектов одного класса, то когда будут уничтожены эти объекты? При выходе из приложения.
Как я это проверил — наследовал вышеуказанный синглтон и просто добавил в него счетчик созданных объектов (в конструктор и деструктор(финализатор)).
Вообще логика мне ваша понятна, но в данном случае я предпочитаю сделать так, чтобы точно держать в памяти 1 объект.
Добавьте в заголовок про то, что это Singleton именно для Unity, пожалуйста.
Кстати ваша реализация не потокобезопасна.
Как минимум вот тут:
Если другой поток вызовет геттер после инициализации поля instance и до завершения работы метода Initialization, он получит недоинициализированный объект.
Ну и присоединяюсь к комментариям выше. Singleton редко когда на самом деле нужен и стоит его избегать. Хотя бывают исключения конечно.
Кстати ваша реализация не потокобезопасна.
Как минимум вот тут:
instance = go.GetComponent<T>();
instance.Initialization();
DontDestroyOnLoad(instance.gameObject);
return instance;
Если другой поток вызовет геттер после инициализации поля instance и до завершения работы метода Initialization, он получит недоинициализированный объект.
Ну и присоединяюсь к комментариям выше. Singleton редко когда на самом деле нужен и стоит его избегать. Хотя бывают исключения конечно.
Да, можно добавить блокировку (как советуют в указанных ресурсах), но это бессмысленно. Однако в Unity все сложно с потоками (половина функционала unity не может быть вызвана не из основного потока), поэтому если и запускать потоки дополнительной обработки — то в Start(). К тому времени все Awake отработают.
А мой класс вывалится с ошибкой даже с блокировкой — по той причине что вызов GetComponent() не может быть совершен не из главного потока. Поэтому ни о какой потокобезопасности и не может быть и речи. Один из вариантов — разделение по времени (то что выше описал).
А мой класс вывалится с ошибкой даже с блокировкой — по той причине что вызов GetComponent() не может быть совершен не из главного потока. Поэтому ни о какой потокобезопасности и не может быть и речи. Один из вариантов — разделение по времени (то что выше описал).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Singleton c учетом подводных камней