Ответ
Основные проблемы связаны с автоматическим подсчетом ссылок (ARC) и циклами сильных ссылок:
-
Retain Cycles (Циклические ссылки) — возникают, когда два или более объекта удерживают друг друга сильными (
strong) ссылками, предотвращая их освобождение ARC.- Типичные сценарии:
- Делегаты без
weak. - Замыкания (
closures), захватывающиеself.
- Делегаты без
- Решение: Использовать
weakилиunownedссылки. - Пример:
class ViewController { var dataLoader: DataLoader? } class DataLoader { // weak предотвращает цикл: ViewController -> DataLoader -> weak -> ViewController weak var delegate: ViewController? }
- Типичные сценарии:
-
Сильное захвата self в замыканиях. Замыкания по умолчанию захватывают переменные сильными ссылками.
- Решение: Использовать список захвата
[weak self]или[unowned self]. - Пример:
networkService.fetchData { [weak self] result in // self стал Optional из-за weak guard let self = self else { return } self.updateUI(with: result) }
- Решение: Использовать список захвата
-
Непреднамеренное удержание объектов (Unintentional Retention). Объекты могут жить дольше необходимого из-за сильных ссылок в коллекциях (например, кэш в виде обычного
Dictionary), глобальных переменных или синглтонах.- Решение: Использовать
NSCache(автоматически очищает память под давлением), тщательнее управлять жизненным циклом.
- Решение: Использовать
-
Утечки в
UIViewController. Контроллер не освобождается после закрытия, часто из-за:- Замыканий (см. п.2).
- Наблюдателей
NotificationCenter, не отписанных вdeinit. - Таймеров (
Timer), не инвалидированных. - Решение: Всегда отписываться и останавливать активность в
deinit.deinit { NotificationCenter.default.removeObserver(self) myTimer?.invalidate() }
Ответ 18+ 🔞
А, ну вот, классика, блядь! Подходишь ты к этому ARC, такой весь из себя умный, думаешь — ну, автоматика же, зашибись! А он тебе, сука, такие сюрпризы подкидывает, что волосы дыбом встают, ёпта!
Первое и главное — это циклические ссылки, они же retain cycles, блядь. Представь: два мужика в баре схватили друг друга за грудки и орут «Ты меня отпусти! — Нет, ты меня отпусти!». И стоят так до скончания времён, потому что оба упёртые, как ослы. В коде так же: два объекта держат друг друга за strong ссылки, и оба в память упёрлись — ни один не сдохнет. Пипец полный.
Где это вылезает? Да везде, блядь! Самые любимые грабли:
-
Делегаты, которые забыли сделать
weak. Ну, классика жанра! Контроллер создал сервис, сервис взял контроллер делегатом сильной ссылкой — и поехали. Контроллер не может умереть, потому что сервис его держит, а сервис не может умереть, потому что контроллер его держит. Замкнутый круг, ебаный в рот. Решение — одно слово:weak. Сделал делегата слабым — и всё, магия, цикл порвался.class DataLoader { // Вот эта вот приписка 'weak' — она как отвёртка, которая откручивает эту ебучую хватку. weak var delegate: ViewController? } -
Замыкания, эти хитрожопые ублюдки. Они по умолчанию хватают всё, что видят, мёртвой хваткой, включая
self. Отправил ты сетевой запрос, передал кложур, а там внутриself.updateUI()— всё, пиши пропало.selfтеперь в плену у этого кложура до конца его дней. А кложур живёт, пока запрос не выполнится. И если экран закрыли, а запрос ещё идёт — экран в памяти болтается, как призрак, блядь. Решение? Список захвата, мать его![weak self]— и сразу жизнь налаживается. Только не забудь потом этогоselfразвернуть, а то будешь обращаться кnilи удивляться, почему ничего не работает.networkService.fetchData { [weak self] result in // Сначала стряхни с себя этот weak, сделай сильным на время. guard let self = self else { return } // Или просто 'self?' в новых свифтах, похуй. self.updateUI(with: result) // Теперь можно работать. } -
Непреднамеренное удержание — это вообще песня. Завёл ты себе кэшик в виде простого
Dictionary, думаешь — а чё, удобно. А он, сука, всё, что в него положили, держит сильнее бульдога. Картинки там, данные. Память упёрлась в потолок, система начинает убивать твоё приложение, а ты чешешь репу. Решение?NSCache, дружок! Умная штука, которая под давлением сама почистит мусор. Или сам вовремя чисти, не будь свиньёй. -
А контроллеры (
UIViewController) — это отдельная эпопея, ёперный театр! Закрыл экран, а он не деинитится. Сидит в памяти, как маньяк в шкафу. Почему?- Кложуры — уже обсудили, блядь.
- Наблюдатели
NotificationCenter. Подписался вviewDidLoad, а отписаться забыл. Теперь этот труп будет вечно слушать все уведомления в системе. Вdeinitнадо орать:NotificationCenter.default.removeObserver(self)! - Таймеры (
Timer). Запустил, аinvalidate()не вызвал. Он и после смерти экрана тикает, как заведённый. Останови его вdeinit!deinit { // Вот это вот — святое. Уборка за собой. NotificationCenter.default.removeObserver(self) myTimer?.invalidate() print("Наконец-то (self) сдох, ура!") // Для душевного спокойствия. }
Короче, суть в чём, блядь? ARC — он не волшебник, он уборщик. Он убирает только тот мусор, на который никто не показывает пальцем. А если ты намусорил и сам на свой мусор указал сильной ссылкой — извини, дружок, это теперь твой персональный артефакт в памяти до перезагрузки приложения. Думай головой, используй weak/unowned, чисть за собой — и будет тебе счастье. А не будет — welcome to Leaks Instruments, ебать твою в сраку.