В чем разница между Witness Table и Virtual Table (vtable) в Swift?

Ответ

Witness Table (WT) и Virtual Table (VTable) — это механизмы диспетчеризации методов, но они служат разным целям и используются для разных типов.

Сравнительная таблица

Признак Witness Table (Таблица свидетелей) Virtual Table (Виртуальная таблица)
Для чего Диспетчеризация методов протоколов для типов значений (struct, enum) и классов. Диспетчеризация методов классов при наследовании и полиморфизме.
Типы Реализации протоколов (Protocol Witness Table - PWT). Иерархии классов.
Время создания Генерируется во время компиляции для каждого типа, реализующего протокол. Создается во время компиляции и хранится в метаданных класса.
Динамичность Статична для конкретного типа и протокола. Динамична: для экземпляра класса вызывается метод из таблицы его фактического (runtime) типа.
Основа Реализация требований протокола. Механизм наследования и переопределения методов.

Примеры

// 1. Witness Table для протокола
protocol Drawable {
    func draw()
}
struct Circle: Drawable {
    // Компилятор создает Witness Table для Circle, реализующего Drawable.
    func draw() { print("Drawing a circle") }
}
let shape: Drawable = Circle()
shape.draw() // Вызов через Witness Table для Circle -> Drawable

// 2. Virtual Table для классов
class Animal {
    func makeSound() { print("Some sound") } // Запись в vtable Animal
}
class Dog: Animal {
    override func makeSound() { print("Woof!") } // Запись в vtable Dog
}
let pet: Animal = Dog() // Статический тип Animal, динамический - Dog
pet.makeSound() // Вызов через vtable Dog -> выведет "Woof!"

Почему это важно:

  • Witness Tables позволяют типам значениям (struct) участвовать в полиморфизме через протоколы без накладных расходов наследования.
  • Virtual Tables — классический механизм ООП для поддержки наследования и переопределения методов в иерархиях классов.

Ответ 18+ 🔞

А, ну вот, опять эти ваши таблицы, блядь! Слушай, давай разберёмся, как бабка у подъезда, на пальцах, а то голова уже пухнет от этих терминов.

Смотри, есть два способа сказать объекту «сделай что-нибудь», когда ты на самом деле не знаешь, кто он такой. Как будто кричишь в темноту: «Эй, ты там, нарисуйся!» или «Эй, ты там, гавкни!».

Первый способ — для наших, для своих, для классов. Это Virtual Table (VTable). Представь себе, блядь, семью. Отец (базовый класс) умеет makeSound(). Сын (наследник) переучил этот метод, чтобы орать «Гав!». Так вот, VTable — это типа семейного устава, который лежит в шкафу у каждого члена семьи. Когда ты говоришь «эй, папаша, издай звук», а на самом деле это сын в папиной шляпе, он лезет в свой шкаф, в свой устав и делает по-своему. Всё динамически, в рантайме. Классика, ёпта!

class Animal {
    func makeSound() { print("Some sound") } // Запись в папин устав (vtable)
}
class Dog: Animal {
    override func makeSound() { print("Woof!") } // А вот это уже в сыновний устав!
}
let pet: Animal = Dog() // Надел папину шляпу, сука!
pet.makeSound() // Лезу в шкаф к Dog, а там — "Woof!" Пиздец, обман!

Второй способ — для протоколов, для договорняков. Это Witness Table (WTable). Тут другая история, блядь. Это не семья, а типа подряда. Есть контракт — протокол Drawable. В нём пункт: «умеешь рисовать — подписывайся». И вот приходит структура Circle (не класс, а структура, у неё родни-то нет!) и говорит: «Я умею!». Компилятор, такой хитрожопый менеджер, тут же составляет для неё отдельную бумажку-шпаргалку (Witness Table): «Так, если кто спросит про draw() у этого Circle по контракту Drawable — вот тебе адресок, где лежит метод». И эта шпаргалка — статическая, намертво прибита к типу. Никакой динамики, всё честно.

protocol Drawable {
    func draw() // Контракт, один пункт
}
struct Circle: Drawable {
    // Подписал контракт. Компилятор: "Ага, щас тебе шпаргалку сделаю (Witness Table)"
    func draw() { print("Drawing a circle") }
}
let shape: Drawable = Circle() // Смотрите, кто по контракту пришёл!
shape.draw() // Опа, смотрю в шпаргалку для Circle -> Drawable. Так, вот метод. Рисую круг.

Короче, в чём разница, чтобы до конца охуеть?

Что сравниваем Virtual Table (Семейный устав) Witness Table (Шпаргалка по контракту)
Для кого Для классов, у которых есть родня (наследование). Для кого угодно (struct, enum, class), кто контракт подписал (протокол реализовал).
Когда делают Компилятор делает раз и навсегда для каждого класса. Компилятор делает для КАЖДОЙ пары «тип + протокол», который он реализует.
Меняется ли Да, ёбта! У каждого класса в иерархии — своя таблица. Динамично. Нет, блядь! Для Circle и протокола Drawable шпаргалка всегда одна и та же. Статично.
Суть Механизм наследования. «Я сын своего отца, но делаю по-своему». Механизм полиморфизма для протоколов. «Я не твой сын, я фрилансер, но пункты контракта выполню».

И зачем этот весь цирк, спросишь? А затем, чувак, что Witness Tables — это гениальная поебень, которая позволяет нашим любимым структурам (struct) и перечислениям (enum) участвовать в полиморфизме, не становясь классами и не тратясь на наследование. Быстро, чисто, безопасно. А Virtual Tables — это старый, добрый, проверенный способ, как заставить иерархию классов работать, чтобы сын мог обоссать методы отца. Всё просто, как три копейки, если не заморачиваться!