← Назад к вопросам

Как реализован счетчик сильных ссылок под капотом?

3.0 Senior🔥 72 комментариев
#Управление памятью#Язык Swift

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как реализован счетчик сильных ссылок в Objective-C и Swift

Счетчик сильных ссылок — это механизм автоматического подсчета ссылок (ARC) в runtime Objective-C и Swift, который управляет жизненным циклом объектов. Он реализован внутри среды исполнения Objective-C (часть libobjc в macOS/iOS) и компилятора Swift.

Основная реализация в Objective-C

Каждый объект Objective-C (экземпляр класса) содержит в своей структуре скрытое поле retainCount, которое является счетчиком сильных ссылок. Этот счетчик увеличивается при создании сильной ссылки и уменьшается при ее удалении.

Ключевые функции runtime

В runtime Objective-C есть несколько фундаментальных функций:

  • objc_retain() — увеличивает счетчик.
  • objc_release() — уменьшает счетчик; если счетчик становится равен нулю, объект деаллоцируется.
  • objc_autorelease() — помещает объект в autorelease pool.

На уровне компилятора (Clang для Objective-C, Swift compiler для Swift) вызовы этих функций автоматически добавляются в код при компиляции.

Пример Objective-C:

// Компилятор преобразует это в код с явными retain/release
MyClass *obj = [[MyClass alloc] init]; // retainCount = 1
MyClass *strongRef = obj;              // вызывается objc_retain(obj), retainCount = 2
strongRef = nil;                        // вызывается objc_release(obj), retainCount = 1
// Когда obj выходит из scope, вызывается objc_release(obj), retainCount = 0, объект уничтожается

Внутренняя структура счетчика

Счетчик сильных ссылок (retainCount) обычно реализован как 64-битное целое число (в современных системах). Но важно понимать:

  • Не все объекты используют простой счетчик. Для некоторых объектов (например, созданных с помощью alloc или new) счетчик начинается с 1. Для объектов в autorelease pool счетчик может быть больше 1 из-за особенностей пула.
  • Значение retainCount — это деталь реализации. Не рекомендуется использовать его в пользовательском коде (метод retainCount), потому что его значение может быть неожиданным из-за внутренних оптимизаций runtime.

Пример оптимизации: tagged pointers

Для некоторых мелких объектов (например, NSNumber с небольшими значениями) runtime использует tagged pointers — специальный формат, где данные и счетчик ссылок хранятся прямо в значении указателя. В этом случае счетчик реализован иначе.

Реализация в Swift

Swift использует тот же механизм ARC, но с более строгой интеграцией в компилятор. Компилятор Swift анализирует поток данных и добавляет вызовы swift_retain и swift_release (аналоги Objective-C функций) в SIL (Swift Intermediate Language).

Пример Swift:

class MyClass {
    var value: Int
    init(value: Int) { self.value = value }
}

func test() {
    let obj = MyClass(value: 10) // swift_retain
    let strongRef = obj           // swift_retain
    // Когда strongRef и obj выходят из области видимости, вызываются swift_release
}

Компилятор Swift может выполнять более агрессивные оптимизации ARC, например, полностью удалять вызовы retain/release, если объект не покидает локальную область видимости.

Ключевые особенности реализации

  1. Счетчик хранится внутри объекта — обычно в заголовке объекта (objc_object).
  2. Операции атомарны — увеличение и уменьшение счетчика используют атомарные операции для безопасности в многопоточной среде.
  3. Autorelease pools — объекты могут временно помещаться в пул, что влияет на счетчик.
  4. Оптимизации для циклов ссылок — для обнаружения циклов (retain cycles) используется отдельный механизм (например, weak references, которые не увеличивают счетчик).

Что происходит при retainCount == 0

Когда счетчик достигает нуля:

  • Вызывается деаллокатор объекта (dealloc в Objective-C, deinit в Swift).
  • Память объекта помечается как свободная (может быть возвращена системе или сохранена для будущих аллокаций).

Итог

Счетчик сильных ссылок — это интеграция компилятора и runtime, где компилятор автоматически добавляет вызовы функций управления счетчиком, а runtime выполняет атомарные операции над счетчиком и управляет памятью. Это позволяет достичь автоматического управления памятью без накладных расходов полноценного сборщика мусора.

Как реализован счетчик сильных ссылок под капотом? | PrepBro