Ответ
Диспетчеризация (Dispatch) — это механизм, который определяет, какая конкретная реализация метода или функции должна быть выполнена во время вызова. В Swift существует несколько видов диспетчеризации, влияющих на производительность и гибкость.
Три основных типа диспетчеризации:
-
Прямая (Static / Direct Dispatch)
- Как работает: Адрес функции известен компилятору на этапе компиляции. Вызов превращается в простую инструкцию
callпо фиксированному адресу или даже встраивается (inlining). - Самый быстрый вариант.
- Используется для: Глобальных функций, статических методов,
final-методов и методовstruct/enum(кроме протоколов без@objc).struct Calculator { // Прямая диспетчеризация func add(_ a: Int, _ b: Int) -> Int { return a + b } } let calc = Calculator() calc.add(5, 3) // Компилятор точно знает, какую функцию вызывать
- Как работает: Адрес функции известен компилятору на этапе компиляции. Вызов превращается в простую инструкцию
-
Табличная (Table / Virtual Dispatch)
- Как работает: Каждый класс имеет скрытую таблицу виртуальных методов (vtable). При вызове метода runtime ищет его реализацию в таблице класса экземпляра.
- Немного медленнее прямой диспетчеризации (дополнительное обращение к памяти).
- Используется для: Не-
finalметодов классов (стандартное поведение). Это основа полиморфизма.class Animal { func makeSound() { print("Some sound") } // Попадает в vtable } class Dog: Animal { override func makeSound() { print("Woof!") } // Запись в vtable Dog } let pet: Animal = Dog() pet.makeSound() // Runtime смотрит в vtable объекта (Dog) и вызывает Dog.makeSound()
-
Динамическая (Message Dispatch)
- Как работает: Унаследована от Objective-C Runtime. Используется механизм отправки сообщений (
objc_msgSend). Runtime динамически ищет реализацию метода в цепочке классов, поддерживает swizzling и KVO. - Самый гибкий, но самый медленный из-за динамического поиска.
- Используется для: Методов, помеченных
@objc dynamic, всех методов, унаследованных отNSObject.class Observer: NSObject { @objc dynamic var value: String = "" // Включена динамическая диспетчеризация для KVO }
- Как работает: Унаследована от Objective-C Runtime. Используется механизм отправки сообщений (
Как Swift выбирает тип диспетчеризации?
Компилятор Swift стремится использовать прямую диспетчеризацию везде, где это возможно, для максимальной производительности. Он переходит к табличной, а затем к динамической по мере необходимости:
- Структуры и перечисления: Всегда прямая диспетчеризация.
- Классы:
finalкласс илиfinalметод -> Прямая.- Обычный метод не-
finalкласса -> Табличная. - Метод с аннотацией
@objc dynamic-> Динамическая.
- Протоколы:
- Для типов, известных на этапе компиляции (не через протокол) -> может быть прямая.
- При работе через протокольный тип (
let x: Protocol) используется Протокольная таблица свидетелей (Protocol Witness Table, PWT), аналогичная по скорости табличной диспетчеризации.
Практический совет: Для критичного к производительности кода помечайте классы и методы как final, если не планируется наследование или переопределение, чтобы включить прямую диспетчеризацию.