← Назад к вопросам

Приведи пример использования weak self

2.0 Middle🔥 291 комментариев
#Управление памятью

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример использования [weak self] в Swift

[weak self] используется для предотвращения циклов сильных ссылок (strong reference cycles) между объектами, особенно внутри замыканий (closures). Когда замыкание захватывает self как слабую ссылку, это не увеличивает счетчик ссылок на объект, что позволяет избежать утечек памяти.

Ключевые понятия

  • Сильная ссылка: Увеличивает счетчик ссылок на объект, сохраняя его в памяти.
  • Слабая ссылка: Не увеличивает счетчик ссылок; становится nil, когда объект освобождается.
  • Цикл сильных ссылок: Два или более объекта удерживают друг друга через сильные ссылки, препятствуя освобождению памяти.

Пример проблемы: Цикл сильных ссылок

class UserManager {
    var data: [String] = []
    var onUpdate: (() -> Void)?
    
    func setupHandler() {
        onUpdate = {
            self.data.append("Updated") // Сильный захват self
            print("Данные обновлены: \(self.data)")
        }
    }
    
    deinit {
        print("UserManager освобожден")
    }
}

// Использование
var manager: UserManager? = UserManager()
manager?.setupHandler()
manager = nil // UserManager НЕ освобождается, так как onUpdate удерживает сильную ссылку

В этом примере UserManager и замыкание onUpdate удерживают друг друга: manager владеет замыканием onUpdate, а замыкание захватывает self (то есть manager) сильной ссылкой. При попытке освободить manager счетчик ссылок не становится нулевым, и объект остается в памяти, вызывая утечку памяти (memory leak).

Решение с использованием [weak self]

class UserManager {
    var data: [String] = []
    var onUpdate: (() -> Void)?
    
    func setupHandler() {
        onUpdate = { [weak self] in
            guard let self = self else { return }
            self.data.append("Updated")
            print("Данные обновлены: \(self.data)")
        }
    }
    
    deinit {
        print("UserManager освобожден")
    }
}

// Использование
var manager: UserManager? = UserManager()
manager?.setupHandler()
manager = nil // Выведет "UserManager освобожден" в консоль

Что изменилось:

  1. Захват [weak self]: Замыкание использует слабую ссылку на self, не увеличивая счетчик ссылок.
  2. Распаковка через guard let: Поскольку self стал опциональным (UserManager?) внутри замыкания, его необходимо безопасно распаковать. Если объект уже освобожден, self будет nil, и выполнение замыкания прервется.
  3. Освобождение памяти: Теперь manager может быть освобожден, так как замыкание не удерживает сильную ссылку.

Когда использовать [weak self]

  • В асинхронных операциях, таких как сетевые запросы или обработчики таймеров.
  • В замыканиях, которые хранятся как свойства (как в примере выше).
  • В UIKit/ SwiftUI, например, внутри DispatchQueue.main.async или при работе с NotificationCenter.
  • Когда self может быть освобожден до выполнения замыкания.

Альтернатива: [unowned self]

onUpdate = { [unowned self] in
    self.data.append("Updated") // Нет опциональности, но опасно
}

[unowned self] используется, когда объект гарантированно существует на время выполнения замыкания. Если объект освобожден раньше, это приведет к крашу приложения (runtime error). Используйте осторожно, только при полной уверенности в жизненном цикле.

Практические сценарии

Пример с сетью

class NetworkService {
    func fetchData(completion: @escaping ([String]) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
            completion(["Данные 1", "Данные 2"])
        }
    }
}

class ViewController {
    let service = NetworkService()
    var items: [String] = []
    
    func loadData() {
        service.fetchData { [weak self] data in
            DispatchQueue.main.async {
                self?.items = data // Опциональный вызов через self?
                self?.updateUI()
            }
        }
    }
    
    func updateUI() { /* Обновление интерфейса */ }
}

Здесь [weak self] предотвращает утечку, если ViewController будет освобожден до завершения сетевого запроса.

Пример с таймером

class TimerHandler {
    var timer: Timer?
    
    func startTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
            self?.handleTick()
        }
    }
    
    func handleTick() {
        print("Тик")
    }
    
    deinit {
        timer?.invalidate()
        print("TimerHandler освобожден")
    }
}

Выводы

  • Всегда используйте [weak self] в замыканиях, которые могут пережить объект, особенно при работе с асинхронным кодом.
  • Не забывайте распаковывать self через guard let или опциональную цепочку (self?).
  • Избегайте циклов сильных ссылок — это основная причина утечек памяти в iOS.
  • Проверяйте память с помощью инструментов Instruments (Leaks) или Xcode Memory Graph Debugger.