Я понимаю разницу между нетерпеливыми и ленивыми синглтонами: нетерпеливый экземпляр создается при первой загрузке, а ленивый создается при первом использовании (называемый метод).
Теперь, чтобы полностью понять разницу, мы должны знать, когда происходит загрузка.
Согласно этой статье, это происходит на этапе запуска Dart VM. Непосредственно перед созданием и запуском фазы Dart Isolate при вызове метода main.
Вопрос в следующем: когда мы вызываем синглтон из main
void main() async{
await MyService().execute();
runApp( MainApp());
}
разницы между нетерпеливым и ленивым практически нет. Я прав?
Обновлять. Реализации нетерпеливых (ранних) и ленивых синглтонов.
Жаждущий:
class MyService{
MyService._();
static final _instance = MyService._();
factory MyService() => _instance;
}
Ленивый:
class MyService{
MyService._();
static MyService _instance;
factory MyService() => _instance ??= MyService();
}
Обновление 2.
Как пояснил jamesdin в комментариях, Dart имеет ленивую инициализацию статических переменных, и поэтому первый подход предпочтительнее, поскольку он немного короче и не требует дальнейших проверок на ноль.
Все глобальные переменные и переменные static в Dart инициализируются лениво, поэтому я не понимаю, что вы подразумеваете под нетерпеливым синглтоном. Я также не понимаю, какое отношение имеет статья, на которую вы ссылаетесь, к синглтонам. Можете ли вы уточнить?
@AE Полностью согласен, но вряд ли могу себе представить сценарий, когда синглтон не будет использоваться. Сбой приложения? Закрыто пользователем? В обоих случаях об оперативной памяти можно не беспокоиться.
@jamesdlin Я обновил вопрос, чтобы уточнить, что я имею в виду под нетерпеливым и ленивым. Статья не имеет никакого отношения конкретно к синглтонам. Это единственное место, которое я нашел, где указывается, когда классы загружаются в приложение Dart. Можете ли вы сказать, что приведенный выше код практически идентичен, поскольку нетерпеливые все равно будут инициализироваться лениво?
Ваша «ленивая» версия нелегальна. Даже если вы исправите объявление в static MyService? _instance;, предполагая, что ничто другое в библиотеке не обращается к MyService._instance напрямую и не проходит только через конструктор factory, не будет никакой разницы в том, когда объект будет инициализирован. Как я уже говорил, все глобальные переменные и static уже ленивы. То есть они будут инициализированы при первом обращении к ним. Последний метод излишне многословен и усложняет необходимость проверки null при каждом использовании _instance.
@Джеймсдлин. Верно. Зафиксированный. (проблема с быстрой копипастой). Большое спасибо за разъяснения. Я не думаю, что эта информация широко известна. Например, на этот вопрос stackoverflow.com/questions/12649573/… есть ответ с 87 голосами «за» и реализация, аналогичная приведенной выше (с геттером вместо фабрики). В любом случае, мой вопрос больше не имеет смысла. ))) Еще раз спасибо.
@jamesdin Хотите опубликовать то, что вы прокомментировали, в качестве ответа? Я думаю, что многим людям не хватает этих знаний.





Реализация Singleton почти всегда включает глобальные переменные или static, но глобальные и static переменные уже инициализируются лениво при первом обращении к ним. Из тура Dart Language:
Переменные верхнего уровня и класса инициализируются лениво; код инициализации запускается при первом использовании переменной.
Итак, учитывая ваш пример:
Жаждущий:
class MyService{ MyService._(); static final _instance = MyService._(); factory MyService() => _instance; }Ленивый:
class MyService{ MyService._(); static MyService? _instance; factory MyService() => _instance ??= MyService(); }
не будет никакой разницы, когда MyService будет инициализирован (при условии, что все остальное использует конструктор MyService фабрики и не имеет прямого доступа к MyService._instance). В обоих случаях _instance будет лениво инициализирован при первом явном вызове конструктора фабрики после ввода main(). Ни одна из версий на самом деле не рвется.
«Ленивая» версия излишне многословна и добавляет дополнительную проверку на ноль. Как написано, это бесполезно. Однако эта практика может быть полезна в ситуациях, когда вы можете захотеть инициализировать синглтон чем-то другим:
import 'package:meta/meta.dart';
class MyService{
MyService._();
static MyService? _instance;
factory MyService() => _instance ??= MyService();
@visibleForTesting
factory MyService.test(MyService service) {
_instance = service;
}
}
и тогда тесты могут явно использовать MyService.test для инициализации синглтона поддельной или имитационной реализации.
> «Нетерпеливая» версия излишне многословна и добавляет дополнительную проверку на ноль. Опечатка: вместо этого должно быть «лениво».
@YuriyN. Ой, да. Зафиксированный.
между обоими подходами определенно есть разница: представьте, что «вы реализовали Eager Singleton = Early Singleton и никогда его не использовали», что приводит к (одному ненужному объекту в оперативной памяти), который является экземпляром.