> Приведи пример реализации паттерна Singleton в UIKit или Foundation (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Физтех-Центр

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Классический пример - UIApplication.shared или UserDefaults.standard. В UIKit синглтон реализуется через static let shared = ClassName(). Это потокобезопасно, так как Swift гарантирует ленивую инициализацию статических свойств. Для кастомного класса добавляют приватный init(), чтобы запретить создание других экземпляров.

Подробное объяснение

Паттерн Singleton гарантирует единственный экземпляр класса и глобальную точку доступа к нему. В iOS он широко используется для сервисов, менеджеров состояния и фасадов над системными API.

Ключевые элементы реализации:

  • static let shared - ленивая инициализация, потокобезопасность гарантируется компилятором
  • private init() - запрет внешнего создания экземпляров
  • Опционально: final class - запрет наследования, чтобы не появились подклассы с собственными shared

В Foundation и UIKit синглтоны - это NotificationCenter.default, FileManager.default, URLSession.shared, UserDefaults.standard, UIApplication.shared, UIScreen.main.

На практике

В реальных проектах синглтон часто используют для:

  • сетевого слоя (например, APIClient.shared)
  • менеджера авторизации (SessionManager.shared)
  • хранилища настроек или кэша

Важно помнить о trade-off: синглтон усложняет тестирование, так как его сложно замокать. Поэтому в production-коде часто используют внедрение зависимостей, а синглтон оставляют как точку входа для конфигурации.

Пример кода

SWIFT
final class NetworkManager {
static let shared = NetworkManager()
private let session: URLSession
private let baseURL = URL(string: "https://api.example.com")!
private init() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
session = URLSession(configuration: config)
}
func fetchData(from endpoint: String, completion: @escaping (Result<Data, Error>) -> Void) {
let url = baseURL.appendingPathComponent(endpoint)
session.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NSError(domain: "NetworkManager", code: -1)))
return
}
completion(.success(data))
}.resume()
}
}
// Использование
NetworkManager.shared.fetchData(from: "users") { result in
// обработка результата
}

Как отвечать на собеседовании

Начни с определения паттерна и его назначения. Затем покажи реализацию на Swift с пояснением, почему static let - это потокобезопасно. Упомяни системные примеры из UIKit и Foundation. Обязательно добавь про ограничения: приватный init, final class. В конце скажи о недостатках - сложность тестирования и скрытые зависимости - и о том, когда синглтон оправдан, а когда лучше использовать DI.

Что проверяет интервьюер

  • понимание принципов ООП и порождающих паттернов
  • знание особенностей Swift: ленивая инициализация статических свойств, потокобезопасность
  • умение видеть системные примеры в повседневном API
  • осознание последствий использования синглтона в архитектуре
  • способность рассуждать о trade-off и альтернативах

Типичные ошибки

  • использование static var вместо static let - допускает переопределение
  • отсутствие private init() - позволяет создавать новые экземпляры
  • игнорирование потокобезопасности при ручной инициализации через dispatch_once (устаревший подход)
  • создание синглтона для каждой мелочи - антипаттерн
  • забывают про final, что позволяет наследникам ломать инвариант единственности
  • путают синглтон с глобальной переменной или статическим классом

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью