Как можно предотвратить возникновение цикла удержания (retain cycle) в iOS?

Циклы удержания предотвращаются путем правильного использования слабых (weak) или безусловных неудерживающих (unowned) ссылок.

Короткий ответ

Что ответить на собеседовании

Циклы удержания предотвращаются путем правильного использования слабых (weak) или безусловных неудерживающих (unowned) ссылок.

Подробный разбор

Ответ с пояснениями

Циклы удержания предотвращаются путем правильного использования слабых (weak) или безусловных неудерживающих (unowned) ссылок.

Слабые ссылки (weak):

  • Не увеличивают счетчик ссылок объекта.
  • Нулевые (могут стать nil).
  • Используются, когда один объект может существовать независимо от другого (например, делегат).
class DelegateExample {
    weak var delegate: SomeDelegateType? // Слабая ссылка на делегата
}

Безусловные неудерживающие ссылки (unowned):

  • Не увеличивают счетчик ссылок объекта.
  • Гарантированно не нулевые.
  • Используются, когда один объект явно владеет другим, но зависимый объект может существовать только вместе с владельцем (например, замыкания, захватывающие self в классе, где замыкание явно "принадлежит" экземпляру класса). При доступе к unowned ссылке, если объект уже освобожден, происходит ошибка времени выполнения.
class Owner {
    var closure: (() -> Void)?

    func setupClosure() {
        closure = { [unowned self] in // Безусловная неудерживающая ссылка на self
            // Использование self
            print("Using unowned self")
        }
    }
}

Использование в замыканиях: Замыкания могут вызывать циклы удержания, если захватывают экземпляры классов по сильной ссылке, и этот экземпляр класса также содержит сильную ссылку на замыкание. Для предотвращения используются списки захвата (capture lists) в начале замыкания:

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [weak self] in // Слабая ссылка на self
        guard let strongSelf = self else {
            return "" // Обработка случая, когда self уже nil
        }
        if let text = strongSelf.text {
            return "<\(strongSelf.name)>\(text)</\(strongSelf.name)>"
        } else {
            return "<\(strongSelf.name)/>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

Выбор между weak и unowned зависит от семантики отношений между объектами:

  • weak: когда ссылка может стать nil в течение жизненного цикла объекта, на который ссылаются.
  • unowned: когда объект, на который ссылаются, гарантированно существует дольше или столько же, сколько объект, который на него ссылается, и ссылка никогда не станет nil.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее