Pull to refresh

Comments 13

Ну так уже много раз сказано и написано статей(даже на хабре), что синглтон нужно использовать очень осмотрительно, а лучше вообще не использовать. Сначала он выглядит просто и красиво, а чуть поже начинаются приседания. Что ваша статья добавляет ко всему сказаному?
Я добавляю код, который вырос за время работы с этим паттерном. Все расписал в подпунктах, создания, инициализации и уничтожения объектов — я не находил на хабре статей в которых освещались эти моменты.
SinglEton, Карл. SinglE. Исправь, везде сингел.

В заголовке пропустил ещё

Не верю, что ничего подобного по синглтонам на Хабре не было. Даже если не здесь, то по интернетам подобной инфы размазано в изобилии. Любимая цацка начинающих юнитистов же!
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 будет на весь проект. Мы и так кинем сообщение об ошибке, но приложение останется работоспособным и не упадет. И убивать дополнительные инстансы является верным делом, так как они просто будут сидеть в памяти и все. А за счет DontDestroyOnLoad объект не будет почищен уборщиком памяти и просто будет висеть. У меня все приложении должны работать 24/7 без присмотра пользователя, если я оставлю exception, то при вызове какого-нибудь захудалого менеджера раз в 2-3 часа будет вызывать падение приложения. Или писать логику на обработчик ошибок (каждый раз разную). Такие доводы.
Мой комментарий выше был о том, чтобы в первую очередь не создавать лишние сущности — принцип, выполнение которого в данном конкретном случае можно обеспечить с помощью исключений. Вы же не будете индексировать массив с аргументом -1, правильно? Потому что среда выполнения выбросит исключение, а не молчаливо с лицом okay.jpg перетерпит. Вот тут то же самое. И это не что-то неконтролируемое, как, например, кривой JSON, пришедший от сервера, тут всё в Ваших руках. И Вам же будет лучше, не мне.
И да, DontDestroyOnLoad() не имеет отношение к сборщику мусора, если, конечно, Вы его имели в виду.
Смешно сказать, но очень часто индексирую (часто делают в обработке изображений для правильной обработки краев).

Логика в исключении есть — но давайте тогда подумаем, что мы за этим тянем? Дополнительная обработка исключений — кто-то должен отловить и обработать эту ошибку (а кто?). В итоге мы придем к тому же коду, только оформленному иначе.

Касательно 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 = go.GetComponent<T>();
instance.Initialization();
DontDestroyOnLoad(instance.gameObject);
return instance;

Если другой поток вызовет геттер после инициализации поля instance и до завершения работы метода Initialization, он получит недоинициализированный объект.

Ну и присоединяюсь к комментариям выше. Singleton редко когда на самом деле нужен и стоит его избегать. Хотя бывают исключения конечно.
Да, можно добавить блокировку (как советуют в указанных ресурсах), но это бессмысленно. Однако в Unity все сложно с потоками (половина функционала unity не может быть вызвана не из основного потока), поэтому если и запускать потоки дополнительной обработки — то в Start(). К тому времени все Awake отработают.

А мой класс вывалится с ошибкой даже с блокировкой — по той причине что вызов GetComponent() не может быть совершен не из главного потока. Поэтому ни о какой потокобезопасности и не может быть и речи. Один из вариантов — разделение по времени (то что выше описал).
Sign up to leave a comment.

Articles