ARC (Automatic Reference Counting) — это механизм управления памятью в языке программирования Swift, который автоматически отслеживает и управляет использованием памяти для объектов. ARC отслеживает количество ссылок на объект и автоматически освобождает память, когда объект больше не имеет ни одной ссылки на него.
Каждый раз, когда вы создаете новый экземпляр класса (или структуры), ARC увеличивает счетчик ссылок на этот экземпляр. Когда объект больше не нужен (когда количество ссылок становится равным нулю), ARC освобождает память, высвобождая ресурсы, занимаемые объектом.
Механизм основан на принципе «подсчета ссылок». Он позволяет разработчикам сосредоточиться на написании кода, а ARC берет на себя ответственность за управление памятью. Это снижает вероятность утечек памяти и упрощает процесс разработки приложений.
На каком этапе работает ARC
ARC работает на этапе выполнения программы. Во время выполнения ARC отслеживает количество активных ссылок на каждый объект в памяти. Когда количество ссылок на объект становится равным нулю, ARC автоматически освобождает память, занимаемую этим объектом, вызывая деструкторы объектов и возвращая выделенную память обратно в систему.
Система использует подход «подсчета ссылок», который является частью техники управления памятью, известной как сборка мусора. Однако, в отличие от традиционной сборки мусора, которая выполняет сборку периодически или по запросу, ARC освобождает память немедленно, когда объект больше не нужен.
При написании кода на Swift разработчику не требуется явно освобождать память или управлять счетчиками ссылок вручную, поскольку это делает ARC автоматически. Однако, важно понимать, как ARC работает, чтобы избегать утечек памяти или циклических ссылок, которые могут привести к нежелательным проблемам в памяти приложения.
Что делает ARC на этапе компиляции
На этапе компиляции ARC в Swift генерирует инструкции для управления памятью, которые встраиваются непосредственно в скомпилированный код. В частности, ARC определяет, когда должны быть добавлены инструкции для увеличения или уменьшения счетчика ссылок на объекты.
При написании кода на Swift вы можете использовать ключевые слова strong
, weak
и unowned
для явного указания типа ссылки и тем самым контроля над тем, как ARC управляет памятью для объектов.
ARC также выполняет некоторые оптимизации на этапе компиляции для улучшения производительности. Например, он может оптимизировать область видимости объекта и освободить память, когда объект больше не нужен в пределах этой области видимости.
Таким образом, ARC на этапе компиляции играет ключевую роль в генерации кода, который эффективно управляет памятью и обеспечивает безопасность выполнения программы.
ARC в действии
Вот пример того, как работает автоматический подсчет ссылок. Этот пример начинается с простого класса Person
, в котором определено хранимое константное свойство name
:
class Person { let name: String init(name: String) { self.name = name print("\(name) is being initialized") } deinit { print("\(name) is being deinitialized") } }
Класс Person
имеет инициализатор, который устанавливает свойство name
экземпляра и печатает сообщение, указывающее на то, что инициализация идет полным ходом. Класс Person
также имеет деинициализатор, который печатает сообщение, когда экземпляр класса удаляется.
В следующем фрагменте кода определяются три переменные типа Person?
, которые используются для установки нескольких ссылок на новый экземпляр Person
в последующих фрагментах кода. Поскольку эти переменные имеют необязательный тип (Person?
, а не Person
), они автоматически инициализируются значением nil
и в данный момент не ссылаются на экземпляр Person
.
var reference1: Person? var reference2: Person? var reference3: Person?
Теперь вы можете создать новый экземпляр Person и назначить его одной из этих трех переменных:
reference1 = Person(name: "John Appleseed") // Prints "John Appleseed is being initialized"
Обратите внимание, что сообщение «John Appleseed is being initialized» выводится в тот момент, когда вы вызываете инициализатор класса Person
. Это подтверждает, что инициализация произошла.
Поскольку новый экземпляр Person
был присвоен переменной reference1
, теперь существует сильная ссылка от reference1
на новый экземпляр Person
. Поскольку существует по крайней мере одна сильная ссылка, механизм следит за тем, чтобы этот Person
оставался в памяти и не удалялся.
Если вы присвоите один и тот же экземпляр Person
еще двум переменным, будет создано еще две сильные ссылки на этот экземпляр:
reference2 = reference1 reference3 = reference1
Теперь существует три сильные ссылки на этот единственный экземпляр Person
.
Если разрушить две из этих сильных ссылок (включая исходную ссылку), присвоив двум переменным nil
, останется одна сильная ссылка, и экземпляр Person
не будет деаллоцирован:
reference1 = nil reference2 = nil
Система не удаляет экземпляр Person
до тех пор, пока не будет разрушена третья и последняя сильная ссылка, после чего становится ясно, что вы больше не используете экземпляр Person
:
reference3 = nil // Prints "John Appleseed is being deinitialized"