Я хочу создать Swift Singleton без глобального доступа.Шаблон, который я хочу создать, должен гарантировать, что всегда существует только один экземпляр класса, но этот класс не должен быть доступен через обычный глобальный синтаксис MyClass.shared
.Причина этого в том, что я хочу, чтобы класс был полностью и правильно тестируемым (что на самом деле невозможно с глобальными синглетонами).Затем я буду использовать внедрение зависимостей для передачи одного экземпляра из viewcontroller в viewcontroller.Таким образом, проблема «доступа» решается без глобального статического экземпляра.
Что я мог сделать, так это сделать в принципе - ничего.Просто создайте нормальный класс и доверьтесь дисциплине всех разработчиков, чтобы не создавать экземпляры этого класса снова и снова, а использовать его только как зависимость.Но я бы предпочел, чтобы какой-то шаблон с принудительной компиляцией запрещал это.
Так что требование:
- гарантирует, что во время компиляции будет создан только один экземпляр класса
- нет глобального доступа
- гарантия того, что создается только один класс, не должна применяться во время модульных тестов, поэтому можно проверить его правильно
Моя первая попытка решить эту проблему былачто-то вроде этого:
![enter image description here](https://i.stack.imgur.com/el1Xq.png)
class WebService {
private static var instances = 0
init() {
assertSingletonInstance()
}
private func assertSingletonInstance() {
#if DEBUG
if UserDefaults.standard.bool(forKey: UserDefaultsKeys.isUnitTestRunning.rawValue) == false {
WebService.instances += 1
assert(WebService.instances == 1, "Do not create multiple instances of this class. Get it thru the shared dependencies in your module.")
}
#endif
}
}
Примечание: передача аргумента во время запуска создает значение по умолчанию пользователя, которое можно проверить во время выполнения.Вот откуда я знаю, что текущий прогон является модульным тестом.
Обычно этот шаблон работает довольно хорошо.Моя единственная проблема с этим - я должен дублировать этот код снова и снова для каждого возможного синглтона.Что не приятно.Я бы предпочел многоразовое решение.
Singleton Protocol Extension
Одним из решений было создать расширение протокола:
protocol Singleton {
static var instances: Int { get set }
func assertSingletonInstance()
}
extension Singleton {
// Call this assertion in init() to check for multiple instances of one type.
func assertSingletonInstance() {
if UserDefaults.standard.bool(forKey: UserDefaultsKeys.isUnitTestRunning.rawValue) == false {
Self.instances += 1
assert(Self.instances == 1, "Do not create multiple instances of this class. Get it thru the shared dependencies in your module.")
}
#endif
}
}
И затем использовать его следующим образом:
class WebService: Singleton {)
static var instances = 0
init() {
assertSingletonInstance()
}
}
Проблема этого подхода заключается в том, что переменная instances
не private
.Таким образом, кто-то может просто установить эту переменную в 0, прежде чем создавать экземпляр класса, и проверка больше не будет работать.
Базовый класс Singleton
Другой попыткой был базовый класс Singleton
.В этом случае можно использовать private static var instances
.
class Singleton {
private static var instances = 0
required init() {
assertSingletonInstance()
}
private func assertSingletonInstance() {
#if DEBUG
if UserDefaults.standard.bool(forKey: UserDefaultsKeys.isUnitTestRunning.rawValue) == false {
Singleton.instances += 1
assert(Singleton.instances == 1, "Do not create multiple instances of this class. Get it thru the shared dependencies in your module.")
}
#endif
}
}
Проблема этого подхода в том, что он не работает.Инкремент Singleton.instance
добавляет 1 к static instances
типа Singleton
, а не к классу, производному от базового класса Singleton
.
Теперь мне остается либо ничего не делать и полагаться на дисциплину и понимание всех разработчиков или, по крайней мере, использовать расширение протокола с доступом internal
или public
.
Пример реализации можно найти здесь .
Может быть, у кого-то есть лучшие идеи для действительно чистого решения этой проблемы.Я ценю любые намеки или обсуждение по этому поводу.Спасибо.