> Что такое утечки памяти и когда они гарантированно возникают? (iOS, Swift)

Уровень: junior · Роль: mobile · Категория: Технические вопросы

Компании: Eltex

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Утечка памяти - это ситуация, когда объект продолжает занимать память, хотя он больше не нужен приложению. В Swift с ARC утечки гарантированно возникают при наличии сильных ссылочных циклов (retain cycles) между классами, особенно с замыканиями, делегатами или таймерами. Также утечки возможны при неправильном использовании глобальных переменных, статических свойств или захвате self в замыканиях без [weak self].

Подробное объяснение

В Swift управление памятью основано на автоматическом подсчёте ссылок (ARC). Каждый объект класса имеет счётчик сильных ссылок. Когда счётчик падает до нуля, память освобождается. Утечка возникает, когда счётчик никогда не достигает нуля, хотя объект логически больше не нужен.

Гарантированные утечки возникают при:

  1. Retain cycle между двумя объектами - например, класс A сильно ссылается на класс B, а класс B сильно ссылается на класс A. Оба объекта остаются в памяти навсегда.

  2. Замыкания, захватывающие self - если замыкание хранится как свойство объекта и внутри захватывает self сильно, возникает цикл: объект → замыкание → объект.

  3. Таймеры и делегаты - Timer с повторением, который сильно удерживает target, или делегат, объявленный как strong, вместо weak.

  4. Глобальные или статические хранилища - если объект помещается в глобальный массив или статическое свойство, он живёт всё время жизни приложения.

Важно понимать: ARC не убирает утечки автоматически. Он лишь гарантирует, что память освободится, когда сильных ссылок не останется. Задача разработчика - правильно проектировать граф ссылок.

На практике

На iOS утечки чаще всего проявляются в:

  • UIViewController - если контроллер захвачен замыканием или таймером, он не вызывается в deinit, и экран "зависает" в памяти при навигации.
  • Сетевые запросы - completion handler, который сильно захватывает self, удерживает контроллер до завершения запроса.
  • Наблюдатели и NotificationCenter - если не удалить наблюдателя в deinit, объект может остаться в памяти.
  • Core Animation и CADisplayLink - циклы с сильными ссылками на target.

Диагностика: инструмент Instruments (Leaks и Allocations), проверка deinit через логи, использование weak и unowned в замыканиях.

Пример кода

SWIFT
// Пример утечки через retain cycle
class ProfileViewController: UIViewController {
var onSave: (() -> Void)?
func setup() {
// Утечка: замыкание захватывает self сильно
onSave = {
self.saveProfile()
}
}
func saveProfile() {
// ...
}
deinit {
print("ProfileViewController deallocated")
}
}
// Исправление
func setup() {
onSave = { [weak self] in
self?.saveProfile()
}
}
// Пример утечки через таймер
class TimerViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Утечка: таймер сильно удерживает self
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.updateUI()
}
}
deinit {
timer?.invalidate()
print("TimerViewController deallocated")
}
}

Как отвечать на собеседовании

Начни с определения утечки и роли ARC. Затем перечисли основные сценарии гарантированных утечек: retain cycle, замыкания с self, таймеры, глобальные хранилища. Обязательно упомяни, что weak и unowned - основные инструменты для разрыва циклов. Приведи пример из практики: контроллер, который не освобождается после pop. Покажи, как проверить утечку через deinit или Instruments. Для junior достаточно уверенно объяснить базовые случаи и способы исправления.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание ARC и разницы между strong, weak, unowned.
  • Умение находить retain cycle в коде.
  • Знание типичных мест утечек в iOS: замыкания, делегаты, таймеры, NotificationCenter.
  • Практические навыки: как диагностировать и исправлять.
  • Понимание, что unowned опасен, если объект может стать nil, и лучше использовать weak.

Типичные ошибки

  • Путаница между weak и unowned - unowned предполагает, что объект живёт дольше, иначе будет crash.
  • Забывают про [weak self] в замыканиях, которые хранятся как свойства.
  • Не вызывают invalidate() у таймера или не удаляют наблюдателей.
  • Считают, что value types (struct) не могут вызывать утечки - это верно, но они могут содержать классы.
  • Не проверяют deinit при тестировании - утечка остаётся незамеченной.
  • Используют weak для замыканий, когда нужно unowned, или наоборот, без понимания жизненного цикла.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью