Глобальная переменная
Суть
Глобальная переменная объявлена на верхнем уровне файла, вне типов и функций, и доступна из любого места модуля (а с public — и из других модулей). Где именно в файле она стоит — не важно.
Как глобальные переменные работают в Swift
- Ленивая инициализация: глобальная переменная в обычном файле инициализируется при первом обращении, а не при запуске, и это потокобезопасно (под капотом
swift_once, какdispatch_once). Исключение — top-level код вmain.swiftи playground: там переменные инициализируются по порядку сверху вниз. - Swift 6: изменяемая глобальная
varбез изоляции — ошибка компиляции («global variable is not concurrency-safe»). Варианты: сделатьlet(Sendable-типа), изолировать@MainActor, или явно взять ответственностьnonisolated(unsafe).
import Foundation
// Инициализируется лениво при первом обращении, потокобезопасно
let apiBase = URL(string: "https://api.example.com")!
// Swift 6: `var launchCount = 0` — ошибка: global is not concurrency-safe
@MainActor var launchCount = 0 // доступ только с главного актора
nonisolated(unsafe) var legacyFlag = false // «за гонки отвечаю сам»
@MainActor func appDidLaunch() { launchCount += 1 }
Почему глобальное состояние считают плохой практикой
- Скрытая зависимость: функция читает глобал, и по сигнатуре этого не видно — тестировать и переиспользовать сложнее.
- Общее изменяемое состояние — источник гонок данных; Swift 6 заставляет решить это явно.
- Порядок инициализации между модулями не контролируется.
Альтернативы: константы static let внутри типа-пространства имён (enum Config { static let apiBase = ... }), dependency injection, @Environment в SwiftUI, actor для общего изменяемого состояния. Синглтон (static let shared) — тот же глобал, но хотя бы с ленивой инициализацией и пространством имён.
На что смотрит интервьюер
Знаете ли вы про ленивую и потокобезопасную инициализацию (частый вопрос «почему static let shared потокобезопасен?») и что Swift 6 делает с глобальными var.