Нужно ли всегда использовать [unowned self] в замыканиях в Swift?
Когда использовать weak, а когда unowned?
Короткий ответ: нет, не всегда используйте unowned self. На самом деле, в большинстве случаев лучше использовать weak self, а unowned — только в строго определённых ситуациях.
Почему вообще нужны weak и unowned?
Swift использует Automatic Reference Counting (ARC). Когда вы захватываете self внутри замыкания, замыкание сохраняет сильную ссылку на объект. Если объект (например, ViewController) тоже хранит сильную ссылку на это замыкание (через свойство или как обработчик), возникает цикл сильных ссылок (retain cycle) — оба объекта не могут быть освобождены, происходит утечка памяти.
Чтобы разорвать цикл, вы используете weak или unowned:
weak— создаёт слабую ссылку. Объект может быть освобождён в любой момент, и ссылка автоматически станетnil. Поэтому вы всегда работаете с опционалом (self?).unowned— тоже слабая ссылка, но предполагается, что объект никогда не станет nil во время выполнения замыкания. Если он всё-таки станет nil — приложение упадёт с ошибкой.
Когда unowned безопасен?
Только когда вы абсолютно уверены, что объект будет жив всё время, пока существует замыкание. Классический пример:
class MyManager {
var onComplete: (() -> Void)?
func setup() {
// Замыкание живёт только пока жив self (MyManager)
onComplete = { [unowned self] in
self.doSomething() // self точно существует, т.к. замыкание — свойство self
}
}
func doSomething() { print("Done") }
}
Здесь замыкание — это свойство self. Оно не может существовать дольше, чем self. Поэтому unowned безопасен.
Когда unowned ОПАСЕН?
Когда объект может быть освобождён до вызова замыкания. Самый частый случай — асинхронные операции:
class ProfileViewController: UIViewController {
func fetchData() {
APIClient.shared.getData { [unowned self] result in
// !!! КРАШ !!! если пользователь ушёл с экрана, self уже nil
self.updateUI(result)
}
}
}
Пользователь нажал "Назад", контроллер уничтожился, а замыкание всё ещё ждёт ответа от сервера. Когда ответ приходит — self уже нет, приложение падает.
Правило большого пальца
- Всегда начинайте с
weak self— это безопасно. - Используйте
unownedТОЛЬКО когда:- Замыкание — это свойство самого объекта (например, обработчик в классе).
- Вы точно знаете, что объект переживёт замыкание (например, в синхронных вызовах).
- Вы готовы к падению приложения в случае ошибки.
В 95% случаев weak self — правильный выбор. Не бойтесь писать self? — это безопасно и читаемо.
Альтернативный взгляд: используйте unowned только для гарантированно синхронных замыканий
Есть ещё один удобный паттерн — когда замыкание вызывается синхронно, прямо внутри метода. Например, методы работы с коллекциями:
class Calculator {
var values = [1, 2, 3]
func process() {
values.forEach { [unowned self] value in
// forEach выполняется синхронно, self гарантированно жив
self.addValue(value * 2)
}
}
func addValue(_ val: Int) { values.append(val) }
}
Здесь forEach выполняется сразу, не откладываясь. self не может быть освобождён во время итерации, поэтому unowned безопасен и избавляет от лишних опционалов.
Но помните: если в будущем кто-то изменит forEach на асинхронный вызов — код упадёт. Поэтому лично я предпочитаю weak везде, где есть хоть малейшая неопределённость.
Ответов пока нет.
Войдите чтобы оставить ответ