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