Что такое weak-ссылка в Swift и для чего она используется?

«Что такое weak-ссылка в Swift и для чего она используется?» — вопрос из категории Управление памятью, который задают на 32% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

weak — это ключевое слово в Swift, которое создает слабую ссылку на объект. Слабая ссылка не увеличивает счетчик сильных ссылок (retain count) объекта, на который указывает. Это основной механизм для предотвращения циклов сильных ссылок (retain cycles), которые приводят к утечкам памяти.

Ключевые свойства:

  1. Optional: Слабая ссылка всегда должна быть объявлена как переменная optional типа (weak var delegate: SomeDelegate?), потому что объект, на который она ссылается, может быть освобожден в любой момент.
  2. Автоматическое обнуление: Когда объект освобождается, все слабые ссылки на него автоматически устанавливаются в nil. Это предотвращает краш при обращении к освобожденной памяти.

Типичные сценарии использования:

  • Делегаты (Delegates): Классический пример, где объект А не должен владеть объектом Б, который является его делегатом.
  • Замыкания (Closures): При захвате self внутри замыкания, которое хранится долго (например, completion handler).
  • Двунаправленные связи в родительско-дочерних отношениях, где дочерний объект не должен продлевать жизнь родительскому.

Пример предотвращения цикла сильных ссылок с делегатом:

protocol ServiceDelegate: AnyObject { // Протокол должен быть class-bound (`AnyObject`)
    func didReceiveData(_ data: Data)
}

class DataService {
    // Делегат объявлен как weak, чтобы не создавать сильную ссылку на ViewController
    weak var delegate: ServiceDelegate?

    func fetchData() {
        // ... получение данных
        delegate?.didReceiveData(someData) // Безопасный вызов, так как delegate - weak
    }
}

class ViewController: UIViewController, ServiceDelegate {
    let service = DataService()

    override func viewDidLoad() {
        super.viewDidLoad()
        service.delegate = self // ViewController не удерживается сервисом через делегата
    }

    func didReceiveData(_ data: Data) { /* Обработка данных */ }
}
// Когда ViewController освобождается, DataService не удерживает его через delegate.

Правило: Используйте weak для ссылок, которые могут создать цикл, где объект А ссылается на Б, а Б ссылается на А, и ни один из них не должен владеть другим.