Паттерн singleton (одиночка) — популярная техника создания объекта, который существует в единственном экземпляре на приложение. В Swift реализация паттерна упростилась благодаря поддержке статических свойств и потокобезопасности.
История вопроса:
В Objective-C реализация Singleton требовала длинного и нетривиального кода, связанного с потокобезопасностью. В Swift эта проблема решена благодаря ленивой инициализации статических свойств.
Проблема:
Singleton часто используют для централизованного доступа к состоянию приложения (например, Session, конфигурации, сервисы), но неправильное использование приводит к неявным зависимостям и снижению тестируемости кода.
Решение:
В Swift паттерн реализуется через статическую константу типа. Это гарантирует потокобезопасность и однозначность:
Пример кода:
final class Logger { static let shared = Logger() private init() {} func log(_ message: String) { print(message) } } Logger.shared.log("Пример работы Singleton")
Ключевые особенности:
Можно ли (и нужно ли) всегда использовать singleton для любых сервисов приложения?
Нет. Singleton обоснован для truly global state (например, ApplicationSettings), но использовать его для сервисов бизнес-логики неправильно: возникают tight coupling и проблемы unit-тестирования.
Обеспечивает ли static let потокобезопасную инициализацию Singleton в Swift?
Да, начиная с Swift 1.2 (и из-за архитектуры runtime), static let thread-safe по своей природе — инициируется однократно даже при конкуренции потоков.
Может ли singleton быть наследуемым?
Нет. Лучше объявить класс как final, чтобы избежать наследования — иначе возможно создание двух экземпляров singletons разных классов-наследников, что нарушает саму идею паттерна.
Сервис работы с сетью реализован через singleton, и методы используют глобальное состояние. В unit-тестах появляется неявная зависимость, переиспользовать сервис невозможно.
Плюсы:
Минусы:
Singleton используется только для truly-global сущности — например, ApplicationConfig. Все сервисы получают зависимости через явный инжектор.
Плюсы:
Минусы: