Ответ
Static Dispatch (прямой вызов) используется, когда компилятор может однозначно определить, какая реализация метода должна быть выполнена, на этапе компиляции.
Условия для Static Dispatch:
- Методы структур и перечислений: Все методы value types используют статическую диспетчеризацию.
finalметоды и классы: Классы или методы, помеченные ключевым словомfinal, не могут быть переопределены.privateиfileprivateметоды: Если метод не виден за пределами файла/объявления, компилятор гарантирует отсутствие переопределений.- Методы, не входящие в протокол или не переопределенные: В классах, где полиморфизм не задействован.
Пример:
struct MyStruct {
func work() { print("Struct work") } // Static Dispatch
}
final class FinalClass {
func work() { print("Final class work") } // Static Dispatch
}
class Parent {
private func hidden() {} // Static Dispatch
func normal() {} // *Может* быть Dynamic Dispatch, если класс не final
}
let myStruct = MyStruct()
myStruct.work() // Компилятор напрямую вставляет вызов `MyStruct.work`
Преимущество: Исключаются накладные расходы на поиск в таблице виртуальных методов (vtable), что делает вызов быстрее и позволяет проводить агрессивные оптимизации (инлайнинг).
Ответ 18+ 🔞
Давай разберем эту дичь про диспетчеризацию, чтобы не было мучительно больно за производительность твоего кода.
Вот смотри, есть у тебя Static Dispatch — это как позвонить конкретному человеку по номеру, который ты знаешь наизусть. Никаких поисков, дозвонов через секретаря, чистая механика. Компилятор, этот дотошный зануда, уже на этапе сборки чётко понимает, какую именно функцию вызывать. Никаких сюрпризов в рантайме.
Когда эта прямолинейная хуйня срабатывает?
- Структуры и энумы. Это value types, у них наследования нет, они не могут быть разными в рантайме. Всё статично, как твой взгляд на жизнь после пятого стакана.
- Всё, что помечено
final. Сказалfinal classилиfinal func— и всё, приехали. Наследование и переопределение накрылись медным тазом. Компилятор расслабляется и делает статический вызов. - Методы с доступом
private/fileprivate. Логика простая, как три копейки: если кроме этого файла или этого объявления про метод никто не знает, то и переопределить его не могут. Значит, можно оптимизировать. - Обычные методы в классе, который никто не трогал. Пока ты не начинаешь городить огород с наследованием и протоколами, компилятор тоже может использовать прямое обращение.
Смотри, как это выглядит в коде, чтобы не быть голословным:
struct MyStruct {
func work() { print("Struct work") } // Static Dispatch. Точка.
}
final class FinalClass {
func work() { print("Final class work") } // Static Dispatch. Класс final, спорить не о чем.
}
class Parent {
private func hidden() {} // Static Dispatch. Private — значит, инкапсулировано по самое не балуй.
func normal() {} // А вот тут уже *может* начаться Dynamic Dispatch, если от класса наследуются.
}
let myStruct = MyStruct()
myStruct.work() // Компилятор, не моргнув глазом, прямо вставляет вызов `MyStruct.work`. Быстро, дёшево, сердито.
И в чём, собственно, кайф? А кайф в скорости, ёпта! Нет никаких танцев с бубном вокруг таблицы виртуальных методов (vtable), не надо ничего искать в рантайме. Вызов происходит мгновенно. Более того, компилятор, видя такую определённость, может провести жёсткие оптимизации — например, инлайнинг, то есть просто вставить тело функции прямо в место вызова, чтобы вообще не тратить время на сам вызов.
Короче, Static Dispatch — это когда всё ясно, прямо и без фокусов. Идеально для всего, что можно так сделать. Используй, где возможно, и будет тебе счастье в виде быстрого кода.