Вопрос проверяет понимание проблемы утечек памяти в Swift при использовании захвата self в асинхронных замыканиях и знание решения через [weak self].
В Swift, когда вы передаете замыкание (closure) в асинхронную операцию (например, в сетевой запрос или анимацию), это замыкание часто захватывает self для доступа к свойствам и методам текущего объекта. По умолчанию захват происходит по сильной ссылке (strong reference). Это создает риск retain cycle (циклической ссылки), если сам объект также хранит сильную ссылку на это замыкание или на операцию, которая его содержит. В результате память, занятая объектом, никогда не освобождается, вызывая утечку памяти.
Использование [weak self] в списке захвата замыкания создает слабую (weak) ссылку на self. Слабая ссылка не увеличивает счетчик ссылок (retain count) объекта. Если объект (например, ViewController) будет освобожден до выполнения замыкания, self внутри замыкания автоматически станет nil. Это предотвращает утечку, потому что замыкание не удерживает объект в памяти.
Рассмотрим типичный сценарий сетевого запроса в ViewController:
class MyViewController: UIViewController {
let service = NetworkService()
func fetchData() {
// Без [weak self] – потенциальная утечка
service.loadData { data in
self.updateUI(with: data) // Сильный захват self
}
// С [weak self] – безопасно
service.loadData { [weak self] data in
guard let self = self else { return } // Проверяем, жив ли self
self.updateUI(with: data)
}
}
func updateUI(with data: Data) {
// Обновление интерфейса
}
}В первом вызове, если MyViewController будет уничтожен до завершения loadData, замыкание все равно удерживает сильную ссылку на него, и память не освободится. Во втором случае, благодаря [weak self], если ViewController уничтожен, self становится nil, и замыкание просто завершится без действий.
Однако не стоит использовать [weak self] всегда бездумно. Например, в синхронных замыканиях с коротким временем жизни (например, в методах массива map) это излишне, так как цикл не образуется.
Вывод: Используйте [weak self] в асинхронных замыканиях для предотвращения утечек памяти, особенно в iOS-приложениях, где ViewController часто управляют жизненным циклом и могут быть освобождены до завершения фоновых задач. Это ключевая практика для написания стабильных и эффективных приложений на Swift.