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

Как ARC считает ссылки?

2.2 Middle🔥 171 комментариев
#Управление памятью

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

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

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

Как работает подсчёт ссылок в ARC

Automatic Reference Counting (ARC) — это система автоматического управления памятью в Swift и Objective-C, которая отслеживает количество сильных ссылок (strong references) на каждый экземпляр объекта в куче (heap). Механизм подсчёта ссылок является ключевым компонентом ARC.

Основной принцип работы

ARC встраивает код подсчёта ссылок непосредственно в скомпилированную программу. При создании нового сильного указателя на объект счётчик увеличивается, при уничтожении — уменьшается. Когда счётчик достигает нуля, память немедленно освобождается.

class Person {
    let name: String
    init(name: String) { self.name = name }
}

// Создаём объект: Person(), счётчик ссылок = 1
var person1: Person? = Person(name: "Иван") 

// Присваиваем той же ссылке другой объект
// Счётчик для первого объекта уменьшается до 0 -> объект уничтожается
person1 = Person(name: "Мария")

Где и как хранится счётчик

Для каждого экземпляра класса в памяти хранится отдельный счётчик ссылок (reference counter). Обычно он размещается в скрытом заголовке объекта. Счётчик — это целочисленное значение, которое изменяется атомарно для обеспечения потокобезопасности.

Операции с подсчётом ссылок

Увеличение счётчика (retain):

  • При создании объекта через init()
  • При присваивании существующей ссылки новой переменной
  • При передаче объекта в качестве аргумента функции (кроме случаев с inout и weak)

Уменьшение счётчика (release):

  • Когда переменная выходит из области видимости
  • При присваивании переменной другого значения
  • При явном присваивании nil опциональной переменной
func example() {
    // person1: счётчик = 1
    let person1 = Person(name: "Алексей")
    
    // person2 ссылается на тот же объект: счётчик = 2
    let person2 = person1
    
    // person1 выходит из области видимости: счётчик = 1
    // person2 выходит из области видимости: счётчик = 0 -> объект уничтожается
}

Особенности с разными типами ссылок

Сильные ссылки (Strong references)

  • Увеличивают счётчик на 1 при установке
  • Уменьшают счётчик на 1 при удалении
  • По умолчанию все ссылки в Swift являются сильными

Слабые ссылки (Weak references)

  • Не увеличивают счётчик ссылок
  • Автоматически становятся nil, когда объект уничтожается
  • Всегда объявляются как опциональные типы
class Apartment {
    weak var tenant: Person? // Слабая ссылка не увеличивает счётчик
    
    init(tenant: Person?) {
        self.tenant = tenant
    }
}

Бесхозные ссылки (Unowned references)

  • Не увеличивают счётчик ссылок
  • Не становятся nil при уничтожении объекта (обращение к ним вызывает краш)
  • Используются, когда время жизни ссылаемого объекта не короче времени жизни ссылки

Циклические ссылки и их решение

Основная проблема подсчёта ссылок — циклы retain cycle, когда два объекта сильно ссылаются друг на друга, и их счётчики никогда не достигают нуля.

// Проблемный код с циклической ссылкой
class Person {
    var apartment: Apartment? // Сильная ссылка
}

class Apartment {
    var tenant: Person? // Сильная ссылка
}

// Решение: используем weak или unowned
class FixedApartment {
    weak var tenant: Person? // Теперь это слабая ссылка
}

Оптимизации ARC

Компилятор Swift применяет несколько оптимизаций для уменьшения накладных расходов:

  1. Implicit retain/release elimination — удаление избыточных операций retain/release
  2. ARC contraction — объединение последовательных операций
  3. Boundary elimination — оптимизация на границах областей видимости

Отличия от ручного управления (MRC) и сборщика мусора (GC)

В отличие от ручного подсчёта ссылок (Manual Reference Counting), ARC автоматически вставляет вызовы retain/release на этапе компиляции. В отличие от сборщика мусора (Garbage Collector), который работает в фоновом режиме, ARC освобождает память немедленно при достижении счётчиком нуля, что делает его поведение более предсказуемым.

Практические рекомендации

  1. Используйте инструменты анализа — Instruments (Leaks, Allocations) для обнаружения утечек памяти
  2. Проектируйте иерархии объектов с учётом владения — избегайте взаимных сильных ссылок
  3. Применяйте [weak self] в замыканиях, которые могут создавать циклы
  4. Для делегатов обычно используйте weak ссылки, так как делегат обычно не должен владеть делегирующим объектом

ARC существенно упрощает управление памятью по сравнению с ручными методами, но требует понимания механизма подсчёта ссылок для избежания утечек памяти и циклических зависимостей.