ПрограммированиеiOS разработчик

Что такое ARC в Swift? Как избежать retain cycle при работе с closures и делегатами?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

ARC (Automatic Reference Counting) — система автоматического управления памятью в Swift. При каждом новом сильном указателе к объекту счётчик увеличивается; при удалении — уменьшается. Когда счетчик равен нулю — объект освобождается.

Retain cycle (цикл удержания) — ситуация, когда объекты ссылаются друг на друга сильными ссылками и никогда не освобождаются.

Способы избежать:

  • Для делегирующих объектов используйте слабую ссылку:
protocol SomeDelegate: AnyObject { } class Owner { weak var delegate: SomeDelegate? }
  • Для замыканий используйте [weak self] или [unowned self]:
class Example { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doSomething() } } func doSomething() { } }

Вопрос с подвохом.

В чем разница между [weak self] и [unowned self] внутри closure? Какой тип ссылок использовать и когда?

Ответ:

  • [weak self] создает опциональную ссылку; self может быть nil, поэтому обычно используют безопасное извлечение self?.
  • [unowned self] создает не-опциональную ссылку и НЕ увеличивает счетчик; если объект уже освобожден и происходит обращение — будет краш.
  • Выбор зависит от бизнес-логики: если замыкание гарантированно живет не дольше self, можно использовать [unowned self].

Пример:

someClosure = { [weak self] in self?.doTask() } // или (если self 100% живет дольше closure): someClosure = { [unowned self] in doTask() }

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В проекте сложилась ситуация retain cycle между ViewController и его closure, привязанным к обработке нажатия. ViewController ссылался на closure, который захватывал self сильной ссылкой. В результате, контроллер и все его данные не освобождались после закрытия экрана. Проблему решили внедрением [weak self] в closure.


История

Реализация паттерна delegate между двумя объектами. Delegate был объявлен как сильное (strong) свойство. В результате при попытке удалить основной объект из памяти он не деаллоцировался, что привело к утечке памяти. После перевода delegate на weak проблема исчезла.


История

Для анимации использовался closure внутри UIView. Closure захватывал self с помощью [unowned self]. Когда вью удаляли до завершения анимации, приложение крэшилось из-за обращения к уже освобожденному self. Решение — использование [weak self] и обязательная проверка на не-nil self внутри closure.