Что такое Message Dispatch в Swift/Objective-C и приведите пример?

Ответ

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