Может ли объект сильно ссылаться сам на себя и не освобождаться в Swift?
Что проверяют
Понимаете ли вы, что ARC считает ссылки и не умеет искать циклы, в отличие от сборщика мусора. Прямая ссылка на себя тут крайний случай, на деле ждут замыкания, таймеры и делегаты, а следом спрашивают, как такую утечку найти.
Как отвечать
Да, может. ARC освобождает объект, когда счётчик сильных ссылок падает до нуля. Если объект сильно ссылается сам на себя, счётчик нулём не станет, deinit не вызовется, и объект проживёт до конца процесса. Сборщика циклов в Swift нет.
Буквальное self.me = self встречается редко. Реальные формы того же цикла:
- замыкание в свойстве:
self.onTap = { self.close() }. Объект держит замыкание, замыкание держит объект; lazy varс замыканием:lazy var handler: () -> Void = { self.reload() }даёт такой же цикл;Taskв свойстве:self.task = Task { for await x in stream { self.handle(x) } }держит объект, пока идёт бесконечный цикл;Timerсtarget: self: run loop держит таймер, таймер держит объект, пока не вызванinvalidate().
Как разрывать:
[weak self]: ссылка не увеличивает счётчик и становитсяnil, внутри обычноguard let self else { return };[unowned self]: без опционала, но обращение после освобождения объекта роняет приложение. Годится, когда замыкание точно живёт не дольше объекта;weak var delegate: протокол при этом ограничиваютAnyObject;- явный разрыв: обнулить свойство, вызвать
invalidate()илиcancel()в понятный момент.
Искать утечки: Memory Graph Debugger в Xcode, Leaks в Instruments, print в deinit.
Где спотыкаются
Ставят [weak self] везде подряд. Замыкания без @escaping вроде map и разовый DispatchQueue.main.async цикла не создают, там это шум.
Берут unowned ради отсутствия опционала там, где объект может уйти раньше замыкания, например в сетевом колбэке. Итог: краш вместо утечки.
Путают цикл с временным удержанием. Task { self.load() } без сохранения в свойство держит объект только до завершения, это продление жизни, а не утечка.