Ответ
Ключевое слово weak используется в списке захвата замыкания для предотвращения цикла сильных ссылок (retain cycle).
Когда это необходимо?
Используйте [weak self], когда:
- Замыкание хранится как свойство (например, колбэк, обработчик).
selfвладеет этим замыканием (прямо или косвенно).- Замыкание может пережить
self(является@escaping).
Пример проблемы и решения:
class DataLoader {
var onComplete: (() -> Void)? // Хранимое escaping-замыкание
func loadData() {
// БЕЗ weak: ЦИКЛ! DataLoader владеет onComplete, а замыкание сильно ссылается на DataLoader.
// onComplete = { self.handleData() } // Память никогда не освободится.
// С weak: Цикл разорван.
onComplete = { [weak self] in
// self стал опциональным из-за weak
self?.handleData()
}
}
func handleData() { print("Data processed") }
deinit { print("DataLoader deallocated") }
}
Альтернатива unowned:
Используйте [unowned self], только если вы абсолютно уверены, что замыкание не переживет self. В противном случае это приведет к аварийному завершению.
// Допустимо, если замыкание выполняется синхронно в рамках жизни self.
dispatchQueue.async { [unowned self] in
self.updateUI() // Crash, если self к этому моменту освобожден!
}
Лучшая практика: По умолчанию используйте weak для @escaping замыканий и безопасно разворачивайте self с помощью guard:
onComplete = { [weak self] in
guard let self = self else { return }
self.handleData() // self теперь strong reference на время выполнения замыкания
} Ответ 18+ 🔞
А, слушай, вот этот твой weak в замыканиях — это вообще отдельная песня, блядь. Представь себе такую пиздопроебибну: два объекта держат друг друга за жопу, и никто из них не может умереть. Классический retain cycle, ёпта.
Когда эта хрень нужна?
Пиши [weak self], когда:
- Замыкание — это свойство класса, типа колбэка или обработчика. Оно, сука, может жить своей жизнью.
- Сам объект (наш
self) владеет этим замыканием. Получается взаимная любовь, блядь. - Замыкание —
@escaping, то есть оно может сбежать и жить дольше, чем сам объект. Вот тут-то и пиздец.
Смотри, как это выглядит на практике:
class DataLoader {
var onComplete: (() -> Void)? // Хранимое escaping-замыкание
func loadData() {
// БЕЗ weak: ЕБАНЫЙ ЦИКЛ! DataLoader держит onComplete, а замыкание держит DataLoader.
// onComplete = { self.handleData() } // Память нахуй не освободится никогда, welcome to memory leak.
// С weak: Цикл порвали, как Тузик грелку.
onComplete = { [weak self] in
// self стал опциональным, потому что ссылка слабая, ёпта
self?.handleData()
}
}
func handleData() { print("Data processed") }
deinit { print("DataLoader deallocated") }
}
А есть же ещё unowned, эта хитрая жопа.
[unowned self] — это как похуистичная версия weak. Используй её, только если ты на 146% уверен, что замыкание точно умрёт раньше или вместе с self. Иначе — краш, пиздец, и ты будешь искать, кто тебя так научил.
// Ну допустим, если замыкание выполняется тут же и сразу.
dispatchQueue.async { [unowned self] in
self.updateUI() // CRASH, если self к этому моменту уже в тапки дунул!
}
Лайфхак от бывалых:
Просто бери weak для всех @escaping замыканий по умолчанию. И разворачивай его красиво через guard, чтобы не писать self?. в каждой строчке:
onComplete = { [weak self] in
guard let self = self else { return } // Нет self — нет и проблем, в рот меня чих-пых.
self.handleData() // А тут self уже сильная ссылка, но только на время работы замыкания.
}
Вот и вся магия, блядь. Не создавай себе циклов, живи спокойно.