Ответ
Диспетчеризация — это механизм определения того, какая реализация метода должна быть вызвана. В Swift есть два основных типа.
Определения
- Статическая диспетчеризация (Static Dispatch, раннее связывание): Решение о вызываемом методе принимается во время компиляции. Компилятор знает точный адрес функции и может выполнить прямой вызов или даже встраивание (
inline). - Динамическая диспетчеризация (Dynamic Dispatch, позднее связывание): Решение принимается во время выполнения программы. Используется таблица (например, Virtual Table для классов или Witness Table для протоколов) для поиска нужной реализации.
Сравнение
| Характеристика | Статическая диспетчеризация | Динамическая диспетчеризация |
|---|---|---|
| Время разрешения | Время компиляции. | Время выполнения (Runtime). |
| Скорость | Быстрее. Нет накладных расходов на поиск в таблице, возможна оптимизация. | Медленнее. Требуется косвенный вызов через таблицу. |
| Полиморфизм | Не поддерживает полиморфное поведение. | Поддерживает полиморфизм (вызов метода зависит от фактического типа объекта). |
| Гибкость | Менее гибкая. | Более гибкая, основа ООП. |
| Типы в Swift | Структуры (struct), перечисления (enum), final классы и методы, глобальные функции. |
Не-final классы и их методы, вызовы через протоколы. |
Примеры кода
// 1. СТАТИЧЕСКАЯ ДИСПЕТЧЕРИЗАЦИЯ (на примере структуры)
struct Calculator {
// Для структур компилятор использует статическую диспетчеризацию.
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
}
let calc = Calculator()
let result = calc.add(5, 3) // Компилятор ЗНАЕТ, что вызовется Calculator.add
// 2. ДИНАМИЧЕСКАЯ ДИСПЕТЧЕРИЗАЦИЯ (на примере классов)
class Animal {
func speak() { print("...") } // Метод попадает в Virtual Table класса Animal
}
class Dog: Animal {
override func speak() { print("Woof!") } // Метод попадает в Virtual Table класса Dog
}
let myPet: Animal = Dog() // Статический тип Animal, динамический (реальный) - Dog
myPet.speak() // В RUNTIME система смотрит в vtable объекта Dog и вызывает Dog.speak() -> "Woof!"
// 3. ДИНАМИЧЕСКАЯ через протокол (Witness Table)
protocol Drawable {
func draw()
}
struct Circle: Drawable {
func draw() { print("Drawing a circle") }
}
let shape: Drawable = Circle() // Тип протокола
shape.draw() // В RUNTIME вызов через Witness Table для Circle -> "Drawing a circle"
Практический совет: Используйте final для классов и методов, которые не планируется переопределять, чтобы компилятор мог применить статическую диспетчеризацию и оптимизации, повышая производительность.