Что делает механизм ARC на этапе компиляции
Что проверяют
Понимаете ли вы, что ARC это работа компилятора, а не сборщик мусора в рантайме. От этого зависит, сумеете ли вы объяснить утечку на цикле ссылок и не ждать, что среда разберёт её за вас.
Как отвечать
ARC это автоматический подсчёт ссылок. Сам счётчик живёт в рантайме рядом с объектом, а компилятор при сборке расставляет вызовы, которые этот счётчик двигают:
- по правилам владения он вставляет
retainиreleaseтам, где начинается и заканчивается время жизни сильной ссылки; - оптимизатор выбрасывает парные
retainиrelease, которые гарантированно гасят друг друга, поэтому в бинарнике их заметно меньше, чем мест в исходнике; - аннотации
strong,weak,unownedменяют вставляемый код: слабая ссылка регистрируется в отдельной таблице и обнуляется при уничтожении объекта,unownedтакой проверки не несёт и роняет приложение при обращении к мёртвому объекту; - ручные
retainиreleaseв Swift запрещены на уровне компиляции, это ошибка сборки, а не рантайма.
Стоит уточнить область действия: ARC ведёт ссылочные типы, то есть классы и замыкания. Структуры и перечисления копируются по значению, но их поля со ссылками считаются так же. Освобождение детерминировано: как только сильных ссылок не осталось, тут же вызывается deinit, без пауз на сборку.
Где спотыкаются
Говорят, что компилятор считает ссылки. Он не считает и не может: число ссылок известно только в рантайме, компилятор лишь расставляет инкременты и декременты по статически видимому владению.
Второе: обещают, что ARC исключает утечки. Цикл сильных ссылок он не разрывает вовсе, и классический случай с замыканием, захватившим self, лечится списком захвата [weak self] руками.
Третье: приравнивают ARC к сборщику мусора. Сборщик обходит граф объектов в рантайме и умеет собирать циклы ценой пауз, ARC не обходит ничего и пауз не даёт, но платит за это ручной расстановкой слабых ссылок.