Ответ
Message Dispatch (диспетчеризация сообщений) — это механизм динамического вызова методов в Objective-C runtime, где выбор реализации метода происходит во время выполнения программы путем отправки "сообщения" (селектора) объекту.
Ключевые особенности:
- Самая гибкая, но самая медленная форма диспетчеризации.
- Позволяет динамически добавлять/менять методы (method swizzling).
- Поддерживает
forwardInvocation:для обработки вызовов несуществующих методов.
Пример в Swift (с использованием @objc dynamic):
import Foundation
// Класс должен наследоваться от NSObject для работы с Objective-C runtime
@objc class DynamicSpeaker: NSObject {
// Модификатор `dynamic` заставляет использовать message dispatch
@objc dynamic func greet() {
print("Hello from DynamicSpeaker")
}
}
let speaker = DynamicSpeaker()
// 1. Прямой вызов (все равно будет через message dispatch из-за `dynamic`)
speaker.greet()
// 2. Вызов через селектор (явная отправка сообщения)
let selector = #selector(DynamicSpeaker.greet)
speaker.perform(selector)
// 3. Динамическое изменение реализации метода во время выполнения (method swizzling)
let originalMethod = class_getInstanceMethod(DynamicSpeaker.self, #selector(DynamicSpeaker.greet))!
let swizzledMethod = class_getInstanceMethod(DynamicSpeaker.self, #selector(swizzledGreet))!
method_exchangeImplementations(originalMethod, swizzledMethod)
speaker.greet() // Теперь вызовется swizzledGreet
| Сравнение с другими типами диспетчеризации в Swift: | Тип | Скорость | Где используется |
|---|---|---|---|
| Static (Direct) | Самый быстрый | struct, final class, private методы |
|
| Table (Virtual) | Средняя | Обычные методы в class (vtable) |
|
| Message | Самый медленный | @objc dynamic методы, Objective-C |
Ответ 18+ 🔞
А, ну вот, блядь, классика! Message dispatch, ёпта! Это ж как в старом добром Objective-C, где всё через жопу, но зато гибко, как резиновая женщина в цирке.
Смотри, суть-то проще пареной репы, хоть и звучит умно. Вообрази: у тебя есть объект, и ты ему кричишь: «Эй, мудила, выполни метод greet()!». А он тебе в ответ: «Ага, щас, погоди, я в своей куче методов поищу, какой там у меня greet». И начинает рыскать по своим внутренним спискам селекторов, как пьяный в темноте по карманам ключи ищет. Это и есть отправка сообщения — objc_msgSend под капотом орет на всё runtime-окружение.
Чем хорош этот цирк?
- Гибкость — овердохуища. Можно во время работы программы подменить реализацию любого метода (этот трюк зовётся method swizzling). Захотел —
greet()говорит «Привет», а через секунду он уже матерится. Красота! - Динамичность. Можно слать сообщения методам, которых в коде вроде как и нет, а они потом как-нибудь сами разберутся (через
forwardInvocation:). Это как кричать в пустоту: «Сделай что-нибудь!» — и иногда что-то действительно происходит. - Прямой наследник Objective-C. Вся эта магия из того времени, когда программисты были похрабрее и попьянее.
А чем плох?
- Скорость. Она, блядь, самая медленная из всех. Потому что каждый вызов — это не прямой прыжок на код, а целое расследование: «А есть ли такой метод? А у кого? А может, его переопределили?». По сравнению с прямой диспетчеризацией — как ехать на велосипеде с квадратными колёсами.
Пример на Swift, где мы включаем эту древнюю магию:
import Foundation
// Наследуемся от NSObject, иначе runtime скажет «пошёл нахуй»
@objc class DynamicSpeaker: NSObject {
// Волшебные слова `@objc dynamic` — это наш пропуск в мир message dispatch
@objc dynamic func greet() {
print("Ну здарова, я DynamicSpeaker")
}
// Тайный метод для подмены
@objc dynamic func swizzledGreet() {
print("А теперь я подменённый метод, ёпта! Original был бы: ", terminator: "")
// Вызовем оригинальный, который сейчас уже не оригинальный... голова кругом
self.swizzledGreet() // Будет вызван оригинальный `greet` после сваппа
}
}
let speaker = DynamicSpeaker()
// 1. Просто вызов. Но из-за `dynamic` он всё равно пойдёт через message dispatch
speaker.greet()
// 2. Явная отправка сообщения, как в старые добрые
let selector = #selector(DynamicSpeaker.greet)
speaker.perform(selector)
// 3. А теперь, блядь, самое интересное — подмена на лету (swizzling)!
let originalMethod = class_getInstanceMethod(DynamicSpeaker.self, #selector(DynamicSpeaker.greet))!
let swizzledMethod = class_getInstanceMethod(DynamicSpeaker.self, #selector(swizzledGreet))!
method_exchangeImplementations(originalMethod, swizzledMethod)
speaker.greet() // Выведет: «А теперь я подменённый метод...» и вызовет старый
И чтобы ты совсем не запутался, вот тебе сводная таблица, кто в Swift как бегает:
| Тип диспетчеризации | Скорость | Где юзается |
|---|---|---|
| Static (Direct) | Ракета, самый быстрый | struct, final class, private методы — тут компилятор всё знает заранее. |
| Table (Virtual) | Быстро, но с оглядкой | Обычные методы в class (через таблицу — vtable). Стандарт для классов. |
| Message | Черепаха с бодуна, самый медленный | Только там, где явно сказано: @objc dynamic методы, или всё наследие Objective-C. |
Короче, message dispatch — это мощнейший инструмент, но юзать его надо с умом, а не тыкать везде, где не попадя. Иначе производительность твоего приложения накроется медным тазом, а ты будешь чесать репу, почему всё так тормозит.