Когда инициализируются статические (static) переменные в Swift/Objective-C?

Ответ

Момент инициализации зависит от языка и контекста:

В Swift (глобальные и статические свойства)

  • По умолчанию (ленивая инициализация): При первом обращении. Это потокобезопасно.
  • Использование @objc или dynamic: Может инициализироваться при загрузке класса, аналогично Objective-C.
class MyClass {
    static let shared = MyClass() // Инициализируется при первом вызове MyClass.shared
    static var count: Int = 0     // Инициализируется при первом обращении к MyClass.count
    @objc static let constant = "Value" // Может быть инициализирована на этапе загрузки
}

В Objective-C

  • Статические переменные внутри функций (static): Инициализируются при первом вызове функции.
  • Глобальные статические переменные (вне функций): Инициализируются до вызова main() или в момент загрузки модуля (фаза +load или +initialize).

Важно: Порядок инициализации статических объектов между модулями не гарантирован. Циклические зависимости могут привести к неопределенному поведению.

Ответ 18+ 🔞

А, слушай, вот это тема, про которую можно до утра спорить, пока не посинеешь! Инициализация этих ваших статиков — она ж как погода, блядь: в каждом языке своя, и если не знать нюансов, можно так въебаться, что мало не покажется.

Вот смотри, в Swift обычно всё чинно-благородно, по-ленивому. Объявил ты там static let shared = MyClass() — и спи спокойно. Эта сука просыпается только тогда, когда к ней впервые обращаются. То есть пока ты не тронул MyClass.shared, она как будто и не существует, блядь. И главное — потокобезопасно, не надо никаких своих замков городить, всё за тебя сделали, красота.

Но, ёпта, как только ты начинаешь выёбываться и лепишь @objc или dynamic — всё, понеслась душа в рай. Тут уже может включиться обжорливый режим из Objective-C, и инициализация может случиться чуть ли не при загрузке класса, на старте. Так что с этими штуками осторожнее, а то получишь сюрприз, когда не ждал.

class MyClass {
    static let shared = MyClass() // Спокойный ленивец. Проснётся, только когда позовут по имени.
    static var count: Int = 0     // Та же история, первый доступ — и понеслась.
    @objc static let constant = "Value" // А вот эта уже может вскочить как ошпаренная при загрузке.
}

А теперь переходим в Objective-C, тут уже цирк, блядь, и клоуны без страховки.

Статическая переменная, объявленная внутри функции? Ну, это просто. Она как старый дед на лавочке: сидит, ждёт. Инициализируется ровно в тот момент, когда ты впервые вызываешь эту самую функцию. Раз — и готово.

А вот если она глобальная, объявлена снаружи всех функций... О, ёбушки-воробушки! Тут уже всё серьёзно. Эта тварь инициализируется до того, как твоя программа вообще дойдёт до main()! Или в момент загрузки модуля, в этих тёмных фазах +load или +initialize. То есть она живёт своей жизнью, отдельной от твоего кода, представляешь?

И вот тут главный подвох, на котором все и горят: порядок инициализации между разными модулями или файлами — не гарантирован, блядь, вообще! Ноль! Может так получиться, что твой статик из файла A.m попытается использовать статик из файла B.m, а тот ещё даже не родился. И всё, пиздец, неопределённое поведение, креш, слезами зальёшься. Циклические зависимости — это вообще отдельный вид ада, ядрёна вошь.

Короче, вывод простой: со статиками и глобалами нужно вести себя как с гремучей змеей — знать их повадки и не делать резких движений, а то укусит так, что в рот меня чих-пых.