Как взаимодействуют компоненты в паттерне MVC (Model-View-Controller)?

Ответ

В классическом MVC (как он часто реализуется в Cocoa/Cocoa Touch) взаимодействие происходит по принципу "Controller как посредник".

Роли компонентов:

  • Model: Представляет данные и бизнес-логику. Не имеет ссылок на View или Controller. Уведомляет об изменениях (через KVO, делегаты или уведомления).
  • View: Пассивный слой отображения. Отображает данные, полученные от Controller, и передает пользовательские события (тапы, ввод) обратно в Controller.
  • Controller (ViewController): Координатор. Получает события от View, обрабатывает их, обновляет Model и, в ответ на изменения Model, обновляет View.

Схема потока данных:

   [Действие пользователя]
          ↓
      [Controller] ←------ (обновляет) ------ [Model]
          |                                       |
    (обновляет)                            (уведомляет об изменениях)
          |                                       |
          ↓                                       ↓
      [View]                                 [Controller] → (обновляет) → [View]

Пример на UIKit:

// Model
struct Task {
    var title: String
    var isCompleted: Bool
}

// View (настроена в Storyboard/XIB или кодом)
// Содержит UILabel и UIButton. Кнопка подключена к @IBAction в Controller.

// Controller
class TaskViewController: UIViewController {
    @IBOutlet weak var titleLabel: UILabel!
    var task: Task! // Model

    override func viewDidLoad() {
        super.viewDidLoad()
        updateView() // Начальная настройка View на основе Model
    }

    @IBAction func toggleCompletion(_ sender: UIButton) {
        // Обработка события от View, изменение Model
        task.isCompleted.toggle()
        updateView() // Обновление View на основе измененной Model
    }

    private func updateView() {
        // Controller обновляет View данными из Model
        titleLabel.text = task.title
        titleLabel.textColor = task.isCompleted ? .gray : .black
    }
}

Основная проблема (Massive ViewController): Controller становится "мусорным ведром" для логики отображения, сетевых запросов, валидации данных и навигации, что затрудняет тестирование и поддержку.

Ответ 18+ 🔞

А, ну это классика, блядь! Сейчас объясню, как эта штука работает, а то некоторые до сих пор думают, что контроллер — это бог, царь и воин, который должен всё на себе тащить. Ёпта, нет же!

Смотри, в этом самом MVC, который Apple любит, всё по полочкам разложено, но потом всё равно превращается в пиздец.

Кто есть кто в этой басне:

  • Модель (Model): Это типа священная корова, блядь. Данные и правила, как с ними можно делать. Она сама по себе, в рот меня чих-пых! Никаких ссылок на вьюхи или контроллеры — чистота, блядь! Но если в ней что-то поменялось, она кричит об этом на всю деревню (через KVO, делегаты или эти ваши NotificationCenter).
  • Вьюха (View): Тупая, блядь, картинка. Её задача — красиво стоять и показывать то, что ей скормили. Получила тап по кнопке — сразу бежит ябедничать контроллеру: «Хозяин, меня тронули!».
  • Контроллер (ViewController): А вот это, сука, главный распиздяй и посредник! Весь геморрой на нём. Вьюха наябедничала — он бежит менять модель. Модель прокричала, что изменилась — он бежит обновлять вьюху. Бегает, сука, как угорелый, пока не превратится в Massive ViewController — монстра на 1000 строк кода, которого все боятся и ненавидят.

Как это всё бегает по кругу:

   [Пользователь ткнул в экран]
          ↓
   [Контроллер] ←--- (меняет) --- [Модель]
          |                              |
   (обновляет)                     (орёт, что поменялась)
          |                              |
          ↓                              ↓
      [Вьюха]                     [Контроллер] → (бежит обновлять) → [Вьюху]

Вот тебе живой пример, чтоб понятнее было:

// Модель. Простая, как три копейки.
struct Task {
    var title: String
    var isCompleted: Bool // сделано или нет
}

// Вьюха (UIButton, UILabel) — её в сториборде накидали, она тупая и ждёт команд.

// Контроллер. Тут начинается магия и, потенциально, пиздец.
class TaskViewController: UIViewController {
    @IBOutlet weak var titleLabel: UILabel! // Привязанная тупая вьюха
    var task: Task! // Наша священная модель, блядь

    override func viewDidLoad() {
        super.viewDidLoad()
        updateView() // Первый раз показываем, что у нас есть
    }

    // Сюда прилетает крик от вьюхи: "Меня ткнули, блядь!"
    @IBAction func toggleCompletion(_ sender: UIButton) {
        // Контроллер лезет в модель и меняет её
        task.isCompleted.toggle()
        // А потом бежит обновлять вьюху
        updateView()
    }

    private func updateView() {
        // Контроллер, как официант, тащит данные из модели и кладёт на вьюху
        titleLabel.text = task.title
        titleLabel.textColor = task.isCompleted ? .gray : .black
    }
}

И вот в чём, сука, главная проблема-то! Этот контроллер — он же не только этим занимается! К нему ещё прилипает логика отображения, сетевые запросы, проверка данных, переходы между экранами... Короче, всё, блядь! Он раздувается, как пузырь, и в итоге в нём нихуя не разберёшься, про тестирование вообще молчу. Получается не контроллер, а манда с ушами, в которую все скидывают свой код. Вот так-то!