Какие типы диспетчеризации методов существуют в Swift?

Ответ

Диспетчеризация — это механизм выбора конкретной реализации метода для вызова. В Swift представлены следующие типы (от самого быстрого к самому медленному):

  1. Статическая (Static Dispatch)

    • Решение принимается на этапе компиляции.
    • Используется для: обычных функций, struct/enum методов, final методов и классов, private методов.
    • Пример:
      struct Calculator {
          func add(_ a: Int, _ b: Int) -> Int { a + b } // Статическая диспетчеризация
      }
  2. Диспетчеризация через таблицу свидетельств (Protocol Witness Table Dispatch)

    • Используется для методов протоколов и обобщенных (generic) контекстов.
    • Реализация выбирается в runtime на основе конкретного типа, соответствующего протоколу.
    • Пример:
      protocol Drawable { func draw() }
      struct Circle: Drawable { func draw() { print("Drawing circle") } }
      let shape: Drawable = Circle()
      shape.draw() // Вызов через PWT
  3. Динамическая диспетчеризация (Virtual Table Dispatch)

    • Классический ООП-подход для нефинальных (non-final) классов.
    • Решение принимается в runtime на основе типа объекта.
    • Пример:
      class Animal { func makeSound() { print("...") } }
      class Dog: Animal { override func makeSound() { print("Woof!") } }
      let pet: Animal = Dog()
      pet.makeSound() // Вызов через v-table -> "Woof!"
  4. Диспетчеризация сообщений (Objective-C Message Dispatch)

    • Полностью динамическая, используется только для элементов, помеченных @objc и dynamic.
    • Позволяет делать method swizzling и отвечает на responds(to:).
    • Пример:
      class ObjCClass: NSObject {
          @objc dynamic func message() { print("Dynamic message") }
      }

Ответ 18+ 🔞

А, слушай, про диспетчеризацию в Swift, да? Ну это, блядь, такая тема, где компилятор решает, какую конкретно функцию ему запулить, когда ты пишешь someObject.someMethod(). И вариантов этих, как собак нерезаных, целых четыре, от быстрого как хуй с горы до медленного, как черепаха в соплях.

Начнём с самого шустрого, чтоб мозги не закипели сразу.

1. Статическая (Static Dispatch) Это когда всё решается ещё на берегу, то есть на этапе компиляции. Компилятор смотрит на код, видит точный тип и говорит: «А, так это же функция add у структуры Calculator! Понял, принял, впендюрю вызов прямо сюда, нахуй». Никаких сюрпризов в рантайме. Используется для всего простого и прямолинейного: обычные функции, методы структур и энумов, final методы в классах (которые переопределить нельзя, пидарасам), private методы. Всё ясно, как божий день.

struct Calculator {
    func add(_ a: Int, _ b: Int) -> Int { a + b } // Вот тут компилятор уже всё порешил. Статика, детка.
}

2. Диспетчеризация через таблицу свидетельств (Protocol Witness Table, PWT) А вот тут уже начинается магия, но ещё не полный пиздец. Когда ты работаешь с протоколами или дженериками, компилятор заранее не знает, какой конкретно тип приползёт. «О, Drawable? — думает он. — Ну окей, у меня для каждого типа, который подписан на этот протокол, есть своя маленькая табличка (witness table), где записано, какой у него draw вызывать». Решение принимается в рантайме, но оно всё ещё быстрое и предсказуемое, потому что табличка-то уже готова.

protocol Drawable { func draw() }
struct Circle: Drawable { func draw() { print("Рисую кружочек") } }
let shape: Drawable = Circle()
shape.draw() // Рантайм смотрит в табличку для Circle и — опа! — вызывает правильный draw.

3. Динамическая диспетчеризация (Virtual Table, v-table) Классика жанра, ебать его в сраку! Старый добрый ООП-подход из Java и C++. Для нефинальных классов. У каждого класса есть своя виртуальная таблица — список указателей на его методы. Когда ты вызываешь pet.makeSound(), система бежит смотреть: «Так, объект у нас типа Dog? Ага. Значит, в таблице для Dog ищем makeSound. Нашёл! Вот этот указатель, нахуй!». Решение тоже в рантайме, но чуть тяжеловеснее, чем PWT, потому что иерархии наследования могут быть, блядь, овердохуища.

class Animal { func makeSound() { print("...") } }
class Dog: Animal { override func makeSound() { print("Гав-гав!") } }
let pet: Animal = Dog()
pet.makeSound() // V-Table говорит: «Да это же собака! Гавкать!»

4. Диспетчеризация сообщений (Objective-C Message Dispatch) А это, сука, полный дикий запад, наследие Objective-C. Медленнее всех, но даёт такую гибкость, что мама не горюй. Используется ТОЛЬКО для методов, помеченных @objc dynamic. Тут система не ищет в таблицах, а в рантайме сла́вит в объект сообщение: «Эй, чувак, у тебя есть метод message?». Объект может сказать «Ага, есть, держи!», а может и промолчать. Позволяет делать такие трюки, как подмена методов (method swizzling) на лету. Мощно, но дорого.

class ObjCClass: NSObject {
    @objc dynamic func message() { print("Динамическое сообщение, ёпта") } // Полная дичь, полёт в космос.
}

Вот и весь расклад, блядь. От «всё ясно как дважды два» до «чё происходит, я в ахуе». Выбирай, что по душе, но помни: чем статичнее, тем быстрее.