Sobes.tech
Back to questions
Middle
94
questionbank

Как можно предотвратить образование retain cycle в iOS?

Sobes Copilot

Get answers in real time

Answer from AI

sobes.tech AI

Использовать weak или unowned ссылки.

Weak Reference:

  • Не увеличивает счетчик ссылок объекта.
  • Автоматически устанавливается в nil, когда объект, на который он указывает, деаллоцирован.
  • Используется для предотвращения циклов при наличии опциональной связи или при отсутствии гарантии существования объекта-владельца.
  • Объявляется как опционал (weak var delegate: MyDelegate?).

Unowned Reference:

  • Не увеличивает счетчик ссылок объекта.
  • Не устанавливается в nil при деаллокации связанного объекта.
  • Программа завершится с ошибкой выполнения, если попытаться получить доступ к объекту через unowned ссылку после его деаллокации.
  • Используется, когда объект-владелец и объект, на который ссылаются, имеют одинаковый срок жизни или связанный объект гарантированно существует, пока существует владелец.
  • Объявляется как не-опционал (unowned var owner: MyObject).

Пример:

class Owner {
    var closure: (() -> Void)?

    func setupClosure() {
        // Без capture list, self будет сильно захвачен.
        // closure = {
        //     print(self)
        // }

        // Предотвращение цикла с weak
        closure = { [weak self] in
            guard let self = self else { return } // Проверка на nil
            print(self)
        }

        // Предотвращение цикла с unowned
        // closure = { [unowned self] in
        //     print(self) // Не требует проверки на nil, но опасно при деаллокации
        // }
    }

    deinit {
        print("Owner deinitialized")
    }
}

class Controller {
    var owner: Owner?

    deinit {
        print("Controller deinitialized")
    }
}

Сценарии использования:

  • Делегаты: Практически всегда используются weak ссылки для предотвращения цикла между владельцем и делегатом.
  • Замыкания/Блоки: Использование capture list ([weak self] или [unowned self]) внутри замыканий, которые захватывают self.
  • IBOutlet: Обычно не вызывают циклов, так как View контроллер владеет представлениями, а не наоборот.
  • Parent-Child связи: Если дочерний объект имеет сильную ссылку на родителя, но родитель имеет сильную ссылку на дочерний, может возникнуть цикл. В таких случаях родитель может иметь сильную ссылку на дочерний, а дочерний иметь weak ссылку на родителя.

Инструменты для обнаружения:

  • Memory Graph Debugger в Xcode.
  • Профилировщик Instruments (Allocations).
  • Понимание жизненного цикла объектов в приложении.