В архитектуре MVP (iOS), кто должен удерживать сильную ссылку на Presenter?

Ответ

В канонической реализации MVP (Model-View-Presenter) для iOS сильную ссылку на Presenter держит View (ViewController). Presenter, в свою очередь, держит слабую (weak) ссылку на View для предотвращения цикла сильных ссылок (retain cycle).

Почему такая схема?

  • View создает и управляет жизненным циклом Presenter. Без сильной ссылки Presenter будет немедленно деаллоцирован.
  • Presenter ссылается на View для обновления UI. Слабая ссылка гарантирует, что если View (например, ViewController) будет уничтожен, Presenter не будет удерживать его в памяти.

Пример реализации на Swift:

// 1. Протокол View с ограничением `AnyObject` для weak-ссылки
protocol MainViewProtocol: AnyObject {
    func updateData(with items: [String])
}

// 2. Presenter со слабой ссылкой на View
class MainPresenter {
    weak var view: MainViewProtocol?
    private let service: DataService

    init(view: MainViewProtocol, service: DataService) {
        self.view = view
        self.service = service
    }

    func loadData() {
        service.fetchItems { [weak self] items in
            self?.view?.updateData(with: items)
        }
    }
}

// 3. ViewController (View) с сильной ссылкой на Presenter
class MainViewController: UIViewController, MainViewProtocol {
    var presenter: MainPresenter! // Сильная ссылка

    override func viewDidLoad() {
        super.viewDidLoad()
        let service = DataService()
        // View создает и хранит Presenter
        presenter = MainPresenter(view: self, service: service)
        presenter.loadData()
    }

    func updateData(with items: [String]) {
        // Обновление UI
    }
}

Ключевой вывод: Жизненный цикл Presenter зависит от View, а не наоборот.

Ответ 18+ 🔞

О, слушай, а вот этот ваш MVP, блядь! Ну классика жанра, ёпта. Все его обсуждают, а половина нихуя не понимает, где там что держит и кто кого ебёт.

Так вот, смотри, чтобы не было цикла сильных ссылок, который тебе всю память сожрёт, как голодный таракан крошки, нужно сделать всё по феншую.

Кто кого держит?

  • Вьюха (ViewController) — это, блядь, главный барин. Она создаёт презентера и держит его за жопу сильной ссылкой (strong). Потому что если она его отпустит, он тут же нахуй деаллоцируется, как последний алкаш на утреннике. Ему же жить-то надо, пока вьюха жива!
  • Презентер — это уже такой хитрожопый управленец. Он, конечно, должен вьюху обновлять, но держит он её за слабую ручку (weak). Почему? А представь, вьюху закрыли, на другой экран перешли, а этот упырь продолжает её держать сильной хваткой и не отпускает в память вечную. Кринж, да? Слабая ссылка — это как сказать: «Да, я про тебя помню, но если тебя система приберет, я не буду плакать и держать твой труп».

Код, чтоб ты понимал, о какой хуйне я:

// 1. Протокол для Вьюхи. `AnyObject` — это магическая хуйня, чтобы weak можно было сделать.
protocol MainViewProtocol: AnyObject {
    func updateData(with items: [String])
}

// 2. Сам презентер, хитрая жопа.
class MainPresenter {
    weak var view: MainViewProtocol? // СЛАБАЯ ссылка, Карл! Чтобы не держать покойника.
    private let service: DataService

    init(view: MainViewProtocol, service: DataService) {
        self.view = view
        self.service = service
    }

    func loadData() {
        service.fetchItems { [weak self] items in // И тут weak self, чтобы самому не зациклиться.
            self?.view?.updateData(with: items) // Аккуратно так, на всякий пожарный.
        }
    }
}

// 3. Вьюконтроллер, который всем заправляет.
class MainViewController: UIViewController, MainViewProtocol {
    var presenter: MainPresenter! // ВОТ ОНА, СИЛЬНАЯ ссылка! Презентер жив, пока жив этот экран.

    override func viewDidLoad() {
        super.viewDidLoad()
        let service = DataService()
        // Вьюха рожает презентера и говорит: «Живи, сынок, я тебя прокормлю».
        presenter = MainPresenter(view: self, service: service)
        presenter.loadData()
    }

    func updateData(with items: [String]) {
        // Ну тут уже твой UI обновляй, как знаешь.
    }
}

И главный вывод, который ты должен вынести, как икону из горящей избы: Презентер — это приложение к вьюхе, а не наоборот. Живёт он ровно до тех пор, пока живёт экран. А пытаться сделать наоборот — это прямой путь в ад утечек памяти, ёпта. Всё, вопрос закрыт, иди работай.